study in - https://www.programiz.com/python-programming/property
OOP에서 getter setter는 필수 그러나 소스들이 많이 증가하고 더러워진다.
@property를 이용하여 더욱 더 간단하게 할 수 있다!
들어가기 전 attribute들을 setting, getting 하는 여러 기본 소스들과 팁들을 배워보자
class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
__dict__라는 dictionary attribute를 이용해보기
print(human.__dict__) == human.__dict__['temperature']
result>> {'temperature': 37}
class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value
파이썬에는 private라는 기능이 실질적으로 없다.
보통 attribute 맨 앞에 언더스코어 _ 를 사용하여 private variable을 만든다.
Note: The private variables don't actually exist in Python. There are simply norms to be followed. The language itself doesn't apply any restrictions.
파이썬에서는 private variable은 java랑 좀 다르다. 따라야 할 몇 가지 규범이 존재 할 뿐이며, 자체만으론 제약을 받지 않는다. 서서히 알아가 보도록 하자
>>> human._temperature = -300>>> human.get_temperature() -300
위 코드에서
아래와같이 getter, setter를 다시 구현해주었기 때문에
obj.temperature 를 obj.get_temperature() , obj.temperature = val 를 obj.set_temperature(val). << 이런식으로
앞으로 코드가 더 더러워지고 늘어날 일만 남았다.
property 사용으로 해결해보자
class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value...") return self._temperature # setter def set_temperature(self, value): print("Setting value...") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature)
--
human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300
result >>
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 31, in <module> File "<string>", line 18, in set_temperature ValueError: Temperature below -273 is not possible
클래스 초기화와 동시에 get, set이 이루어졌으며
human.temperature 호출과 동시에 get_temperature 이 호출되었고
human.to_fahrenheit() 호출에 또 한 번 동시에 get_temperature 이 이루어졌으며
human.temperature = -300과 동시에 set_temperature 가 이루어졌다.
쉽게 이걸 보면 이해가 가능하다
>>> human.temperature Getting value 37>>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
여기서 잠깐
실제 temperature값은 private처리된 즉 _temperature variable에 저장된다. temperature 는 그저 private variable의 인터페이스 역할을 해준다고 보면 된다.
쉬운 이해를 위해
처음에 human = Celsius(37)로 초기화를 하면 _temperature = 37
그 후 to_fahrenheit()를 호출하여 temperature 값을 98.60000001로 바꾼하고 하여도
다시 get_temperature를 하면 37이 출력된다는 말이다.
In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is:
property(fget=None, fset=None, fdel=None, doc=None)
where,
fget is function to get value of the attribute
fset is function to set value of the attribute
fdel is function to delete the attribute
doc is a string (like a comment)
As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.
>>> property() <property object at 0x0000000003239B38>
A property object has three methods, getter(), setter(), and deleter() to specify fget, fset and fdel at a later point. This means, the line:
temperature = property(get_temperature,set_temperature)
can be broken down as:
# make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)
These two pieces of codes are equivalent.
Programmers familiar with Python Decorators can recognize that the above construct can be implemented as decorators.
We can even not define the names get_temperature and set_temperature as they are unnecessary and pollute the class namespace.
For this, we reuse the temperature name while defining our getter and setter functions. Let's look at how to implement this as a decorator:
# Using @property decoratorclass Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value...") return self._temperature @temperature.setter def temperature(self, value): print("Setting value...") if value < -273.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300)
Run Code
Output
Setting value... Getting value... 37 Getting value... 98.60000000000001 Setting value... Traceback (most recent call last): File "<string>", line 29, in <module> File "<string>", line 4, in __init__ File "<string>", line 18, in temperature ValueError: Temperature below -273 is not possible
The above implementation is simple and efficient. It is the recommended way to use property.
시간나면 정리하겠슴다