| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 프로그래머스
- 프로그래머스 입문
- 무작위로 k개의 수 뽑기
- 월부닷컴
- 특정 문자열로 끝나는 가장 긴 문자열 찾기
- 월급쟁이부자들
- 파이썬
- 월부
- 노개북
- l로 만들기
- 프로그래머스 코딩테스트 기초
- 배열 만들기6
- 노마드코더
- 문자열이 몇 번 등장하는지 세기
- 클린코드
- 세 개의 구분자
- 특별한 이차원 배열 1
- 첫번째로 나오는 음수
- 기초
- 프로그래머스 기초
- 열반스쿨기초반
- 배열 만들기3
- 주사위 게임1
- 코딩테스트
- 개발자북클럽
- 풀스택gpt
- 0 떼기
- 부동산강의
- Python
- 열기반
- Today
- Total
해쟈스토리
Property 본문
객체지향 프로그래밍 언어에서 프로퍼티(property)는 객체의 속성을 제어할 때 유용하게 사용되는 기능이며 데이터 캡슐화에 도움이 된다. 그리고 종종 데이터의 정확성이 객체를 생성할 수 있는지 여부를 결정한다. 그러니까 데이터가 특정 값을 가질 경우에만 존재할 수 있고, 잘못된 값을 가진 경우에는 존재할 수 없는 경우가 있다..
이것이 유효성 검사 메서드를 만드는 이유이다.(setter작업)
파이썬에서는 property를 사용하여 setter와 getter 메서드를 더 간결하게 캡슐화할 수 있다.
일반적으로 데이터를 읽어주는 메서드를 getter(게터), 데이터를 변경해주는 메서드를 setter(세터)라고 한다.
위도와 경도는 특정 범위에서만 의미가 있다. 해당 범위를 벗어나는 좌표는 존재할 수 없다. 좌표를 나타내는 객체를 생성할 수 있지만 어떤 값을 사용할 때는 항상 허용 가능한 범위내에 있는지 확인해야 한다. 이런 경우 프로퍼티를 사용할 수 있다.
class Coordinate:
def __init__(self, lat:float, long: float) -> None:
self._latitude = self._longtitude = None
self.latitude = lat
self.longtitude = long
@property
def latitude(self) -> float:
return self._latitude
@lititude.setter
def latitude(self, lat_value: float) -> None
if lat_value not in range(-90, 90+1):
raise ValueError(f"유효하지 않는 위도 값: {lat_value}")
self._latitude = lat_value
@property
def longtitude(self) -> float:
return self._longtitude
@longtitude.setter
def longtitude(self, long_value: float) -> None
if long_value not in range(-90, 90+1):
raise ValueError(f"유효하지 않는 경도 값: {long_value}")
self._longtitude = long_value
이렇게 함으로써 private 변수에 저장된 값을 반환하는 별도의 속성을 만들었다.
@latitude.setter 데코레이터로 선언된 유효성 검사 로직이 자동으로 호출되며 명령문의 오른쪽에 있는 값이 파라미터로 전달된다.
데코레이터를 사용하지 않고 setter와 getter 메소드를 작성하면 어떻게 될까?
class Coordinate:
def __init__(self, lat:float, long: float) -> None:
self._latitude = self._longtitude = None
self.latitude = lat
self.longtitude = long
def get_latitude(self) -> float:
return self._latitude
def set_latitude(self, lat_value: float) -> None
if lat_value not in range(-90, 90+1):
raise ValueError(f"유효하지 않는 위도 값: {lat_value}")
self._latitude = lat_value
def get_longtitude(self) -> float:
return self._longtitude
def set_longtitude(self, long_value: float) -> None
if long_value not in range(-90, 90+1):
raise ValueError(f"유효하지 않는 경도 값: {long_value}")
self._longtitude = long_value
위와 같이 작성되었을 때 아래와 같이 사용할 수 있다.
>>> t = Coordinate(11.12, 33.33)
>>> t.set_latitude(34.3)
>>> t.get_latitude()
34.3
근데 이런 방식은 처음 인스턴스객체를 만들때 유효성 검사가 동작하지 않는다. 11.12, 33.33을 넣었지만 만약 1111.111를 넣어도 범위에서 벗어나지만 set_latitude가 동작하지 않는다.
그리고 이 경우 기존에 `<인스턴스 객체>.속성 = 값` 으로 이미 이 클래스를 어딘가외부부에서 사용하고 있는 코드가 있을 경우 사용법이 .set_latitude() 메소드 사용으로 바뀌어서 유지보수했을때 다른 외부코드도 변경이 일어나야하는 단점이 존재한다.
그래서 사용법 변경이 일어나지 않는 데코레이터를 사용하는 것이 좋다.
객체의 모든 속성에 대해 get_, set_ 메서드를 작성할 필요는 없다. 속성값을 가져오거나 수정할 때 특별한 로직이 필요한 경우에만 프로퍼티를 사용하자.
프로퍼티는 명령-쿼리 분리 원칙(command and query separation - CC08)을 따르기 위한 좋은 방법이다. 명령-쿼리 분리 원칙은 객체의 메서드가 무언가의 상태를 변경하는 커맨드이거나 무언가의 값을 변환하는 쿼리이거나 둘 중에 하나만 수행해야지 둘 다 동시에 수행하면 안 된다는 것이다.
예를 들어 set_email이라는 메서드가 있는데 세팅하고 세팅된 이메일을 반환해서 setter와 getter 역할을 둘다 한다고 치자if self.set_email("test@test.com")처럼 사용했다면 이 코드는 무엇을 의미하는 걸까? "test@test.com"으로 이메일을 설정하려는 걸까? 이미 이메일이 해당값으로 설정되어있는지 확인하려는 걸까?
아니면 동시에 이메일 값을 설정하고 상태가 유효한지 체크하려는 걸까? 동작이 여러개면 이렇듯 헷갈리게 된다.
@property 데코레이터는 무언가에 응답하기 위한 쿼리이고, @<property_name>.setter는 무언가를 하기 위한 커맨드이다.
무언가를 할당하고 유효성 검사를 하고 싶으면 두개 이상의 문장으로 나누어야 한다.
참조 : 유지보수가 쉬운 파이썬 코드를 만드는 비결 파이썬 클린코드 2nd Edition - 마리아노 아나야
'파이썬 > 클린코드' 카테고리의 다른 글
| 결합도 (0) | 2024.08.19 |
|---|---|
| 방어적 프로그래밍 (0) | 2024.08.19 |
| dataclasses 모듈 (1) | 2024.06.17 |
| 파이썬에서의 밑줄 (2) | 2024.06.14 |
| 매직메소드(__enter__와 __exit__, 그리고 contextlib) (0) | 2024.06.13 |