@property의 개념
class Robot:
__population = 0
def __init__(self, name, age):
self.__name = name
self.__age = age
Robot.__population += 1
def __say_hi(self):
print(f"Greetings, my masters call me {self.__name}.")
droid = Robot("R2-D2", 2)
print(droid.__age) #에러
>>> AttributeError: 'Robot' object has no attribute '__age'
클래스 변수와 인스턴스 변수, 인스턴스 메서드를 위와 같이 private 형태로 만들었다고 하자.
이 경우에 droid 인스턴스에 __age로 접근을 해도 private하기 때문에 AttributeError 가 발생한다.
이 때, age에 read는 하고, update나 write을 불가능하게 하게 할 수는 없을까? 정답은 존재한다.
바로 '@property' 라는 데코레이터를 사용하는 것이다.
사용하는 방법은 아래와 같이 선언하고 싶은 메서드 위에 return을 해주는 형태로 이뤄진다.
class Robot:
__population = 0
def __init__(self, name, age):
self.__name = name
self.__age = age
Robot.__population += 1
@property
def age(self):
return self.__age
def __say_hi(self):
print(f"Greetings, my masters call me {self.__name}.")
droid = Robot("R2-D2", 2)
print(droid.age)
>>> 2
선언한 메서드 위에 @property 데코레이터를 붙여주고 인스턴스에 해당 메서드로 접근하면 print가 되는 것을 볼 수 있다.
Setter의 개념
droid.age = 50 #에러
>>> AttributeError: can't set attribute
그리고 age를 바꾸기 위해서 다음과 같이 값을 할당하게 되면 'AttributeError: can't set attribute' 가 난다. getter 라는 property를 통해서 age는 가져올 수 있으나 이 것을 바꿀 setter라는 attribute가 없기 때문이다.
age를 바꾸고 싶을 경우 어떻게 해야할까? 정답을 settet를 사용하면 된다.
setter를 사용하는 방법은 해당하는 property를 적어준후 setter를 적어준다. 그 이후 함수 이름은 동일하게 적고 인자로 할당할 새로운 값을 적는다. setter를 하는 이유는 직접적으로 set을 하지 말고 setter 안으로 들어와서 처리하는 방식이다.
class Robot:
__population = 0
def __init__(self, name, age):
self.__name = name
self.__age = age
Robot.__population += 1
@property
def age(self):
return self.__age
@age.setter
def age(self, new_age):
print(new_age)
self.__age = new_age
droid = Robot("R2-D2", 2)
print(droid.age)
>>> 2
droid.age = 50
print(droid.age)
>>> 50
setter를 이용했을 때 동작방식은 다음과 같다.
1. droid.age = 50 이라고 한 순간 age 프로퍼티는 set이라는 attribute를 찾기 시작한다
2. set을 찾았을 경우 오른쪽에 어떤것을 대입했는지 찾는다. 이 경우에는 50이다.
3. 50이 바로 new_age 인자로 넘어가게 된다(setter 안의 new_age를 출력할 경우 50이 된다)
4. 안에서는 private을 사용할 수 있기 때문에 new_age를 할당해준다.
그럼 여기서 의문이 들 수 있다.
droid.age = -100
결국 접근할 수 있는데 은닉이 안되는 것이 아니냐?
바로 답은 setter에 값에 조건문을 넣어서 원하지 않는 값이 할당되는 것을 막을 수 있다.
class Robot:
__population = 0
def __init__(self, name, age):
self.__name = name
self.__age = age
Robot.__population += 1
@property
def age(self):
return self.__age
@age.setter
def age(self, new_age):
if new_age < 0:
raise TypeError("invaild range")
print(new_age)
self.__age = new_age
droid = Robot("R2-D2", 2)
droid.age = -100 # 에러
>>> TypeError: invaild range
의도하지 않은 값을 할당할 경우 setter의 if문에 걸리게 되어 Error를 뱉어내줌으로써 원하지 않는 값을 할당하는 것을 막을 수 있다.
은닉의 이점
1. 위와같이, 인스턴스 변수 값에 대한 유효성 검사 및 수정을 막을 수 있다
2. 인스턴스 변수 값을 사용해서 적절한 값으로 보내고 싶을 때
인스턴스 변수 값을 사용해서 적절한 값으로 보내고 싶을 때라는 의미는 다음과 같다.
예를 들어 클래스 밖 외부에서 속성을 출력하고 싶을 때 특정 단어를 붙이고 싶을 때 다음과 같이 정의할 수 있다
class Robot:
__population = 0
def __init__(self, name, age):
self.__name = name
self.__age = age
Robot.__population += 1
@property
def name(self):
return f"min {self.__name}"
droid = Robot("R2-D2", 2)
print(droid.name)
>>> min R2-D2
이렇게 되면 간접적으로 property를 사용해서 접근할 수 있게 되고 다음과 같이 원하는 출력의 형태로 변경할 수 있다.
'Language > Python' 카테고리의 다른 글
[Python] Type Hint, Callable types (0) | 2021.12.19 |
---|---|
[Python] 컴포지션(composition) (0) | 2021.12.16 |
[Python] 네임스페이스(namespace)에 대한 이해 (0) | 2021.12.08 |
[Python] 제네레이터(Generator)를 이용한 itertools 예시 (0) | 2021.12.01 |
[Python] 반복자(Iterator), 제너레이터(Generator)에 대한 이해 (0) | 2021.11.24 |
댓글