About 2 months ago I considered my self an intermediate python user.
This level of knowledge was enough to work on pandas data transformation and use most of the data science API.
However, when it comes to software engineering and developing production, tested code I realized that there was a lot to learn about programming concepts and how that would work with python.
My intermediate knowledge and experience already gave me lots of power but when I started to dig into source code and also at work when I had to browse to some large code base I felt that I did not know lots of things in python and had to get my s*** together and become a proficient advanced and fluent python user.
I am talking about advanced concepts like property, decorator, inheritance, mro, getters, setters and the other OOP paradigm like interfaces and encapsulation that was necessary to up my game.
In this post I will give an introduction of the property class and property decorator.
When someone start learning about classes one of the first concepts we learn is class attribute and instance attribute which is not the scope of this post. I assume that this is already a clear concept. If not you can refresh here
The property class helps to manage class attribute where you can control the mutability of an instance property and also set some conditions you want an instance attribute to have.
A classical example which is widely used in many example is temperature.
Say was want to create a class that manages objects temperature and convert between Celsius and Fahrenheit.
The absolute zero temperature is -273 Celsius and you would not want to allow anyone to set a temperature below this value.
A way to manage this is via property.
There are two ways you can do it, i.e. using the
property class itself or the
The documentation doc string in python is very good actually. Lets print it.
Here is an exert of it with a quick example to get you started.
class C(object): def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x x = property(getx, setx, delx, "I'm the 'x' property.")
The property class take 4 arguments and returns the property attribute
property(fget=None, fset=None, fdel=None , doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting
fdel a function for deleting,
The decorator alternative makes it simpler
class C(object): @property def x(self): "I am the 'x' property." return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x
This where just template examples. Lets now write our temperature class that I took from programviz
# Using @property decorator class 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) # this is going to raise an exception as it is below 273.15 # output of the program (commented out) > Setting value... > Getting value... > 37 > Getting value... > 98.60000000000001 > Setting value... Traceback (most recent call last): File "temperature_.py", line 28, in <module> coldest_thing = Celsius(-300) File "temperature_.py", line 3, in __init__ self.temperature = temperature File "temperature_.py", line 17, in temperature raise ValueError("Temperature below -273 is not possible") ValueError: Temperature below -273 is not possible
Note that when an instance of class initialize it firsts sets the value then it gets.
I got confused with this as sometimes I used the property to set an immutable property but on the example it made it mutable with the
If you pay attention to the example on the documentation it does not initiate with a default value and the
x. So let’s implement it as in the help example
class C(object): @property def x(self): "I am the 'x' property." return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x c = C(3) print(c.x) # output Traceback (most recent call last): File "c.py", line 19, in <module> print(c.x) File "c.py", line 6, in x return self._x AttributeError: 'C' object has no attribute '_x'
So if we add the
class C(object): def __init__(self, x=0): self.x = x @property def x(self): "I am the 'x' property." return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x c = C(3) print(c.x) c.x = 23 print(c.x) # output >0 >23
Actually what happened is that when it
__init__ the instance it set the value to the default value.
If you want to make it immutable like having a value that does not change just only implement set method.
Let’s demonstrate it
class C(object): def __init__(self): self._x = 42 @property def x(self): "I am the 'x' property." return self._x c = C() print(c.x) c.x = 23 # output >42 Traceback (most recent call last): File "c.py", line 24, in <module> c.x = 23 AttributeError: can't set attribute
Note that this time on the
__init__ I set the value
42 to the
_x underscore version to set it to a value at initialization and never allow it to change.
I think this is it for property.
If you inspect source code in GitHub you will see it a lot and now you know what is going on when you see it.
2020-09-16 06:03 +0930