Do your own Python's @property
MoRoth
Posted on May 18, 2020
After reading this chapter in "Expert Python Programming" -
Advanced Attrbiute Access patterns - https://subscription.packtpub.com/book/application_development/9781789808896/4/ch04lvl1sec39/advanced-attribute-access-patterns
decided to implement @property partially.
Here is the final result - https://gist.github.com/mzsrtgzr2/46975900b8d7a8e6f8cc51d1a40ca940
class myproperty:
def __init__(self, func):
self.f = func
def setter(self, func):
self.setter_func = func
return self # (1) why do we need this? read below on
def __get__(self, instance, klass):
return self.f(instance or klass)
def __set__(self, instance, value):
self.setter_func(instance, value)
class Foo:
def __init__(self):
self._val=0
@myproperty
def temperature(self):
return self._val
@temperature.setter
def temperature(self, value): # (2) why need same name as getter?
print('setting to', value)
self._val = value
ins = Foo()
print(int.temperature) # 0
ins.temperature = 6
print(int.temperature) # 6
It's important to understand how decorators work. This is the key to understand this code. The decorated function is REPLACED with an instance of your decorator (or a function in case you use a decorator function and not class). That being said, you can add functionality to your decorator class like __get__
and __set__
, like in the code above.
__get__
- doing a "read" operation on the decorator class.
__set__
- doing a "write" operation on the decorator class.
This is another way to look at decorators - it can be used for "data descriptors" (when you access a field, as in ins.temperature
) and not just as function wrappers (implemented with __call__
, as in ins.temperature()
).
Important - why is the decorated set method
should also be named temperature
?
A decorator works like this:
@dec
def func1:
pass
actually converts to
func1 = dec(func1)
so the name of the function is important.
What happens if we don't use the same name in (2) or drop the return self
statement at the end of our decorator: what used to be temperature
in our class is overridden to None
- losing all the functionality we thought we built. We really happens:
class Foo:
def __init__(self):
self._val=0
def temperature(self):
return self._val
temperature = myproperty(temperature) # instantiating myproperty class
def temperature(self, value): # (2) why need same name as getter?
print('setting to', value)
self._val = value
temperature = temperature.setter(temperature)
In this example, the property field temperature
is always the reference name
and can't change it.
Posted on May 18, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.