For a long time, I've not been able to use classes well enough in Python.
Rare resources on the web could help me write classes that are Pythonic. That's because a lot of resources trying to overengineer Python classes.
I used to write methods starting with
set_ to get and set attributes in Python. This practice was similar to the way Java developers write their getters and setters. Ugly and not Pythonic.
Until I knew that there is a better way to do this in a Python way.
In this post, I'll show you how to write @property decorator in Python classes to be Pythonic. It should be easy to understand and use, especially with the step-by-step approach.
The @property getter
Let's first simulate our use case as if @property does not exist.
In the above example, a class
Temperature has a getter method called
get_fahrenheit. It returns the temperature to Fahrenheit.
This is a very typical use case of a getter method.
Now, Fahrenheit looks like a property of a class. You don't need to call it a function like the following:
We can see that we got rid of the
() in the getter method call. Instead, we now have a
@property decorator before
fahrenheit function. This function acts as a getter method.
As a side effect, we no longer need to prepend
get_ to the function name.
Note that the signature of the getter method needs to be
Why do we use @property decorator?
In the previous example, we have a property decorator. This is a decorator we use to add a getter method to a class. We could use the
property function instead like this:
but of course, the
@property decorator syntax is easier.
The @property setter
But the property is not only useful for that. We can also use it to set and delete the value of the attribute. Let's see how we can set a new value for the
class Temperature: def __init__(self, celsius=0): self._celsius = celsius @property def fahrenheit(self): return (self._celsius * 9 / 5) + 32 @fahrenheit.setter def fahrenheit(self, value): if not isinstance(value, int) and not isinstance(value, float): raise TypeError('value must be a number') self._celsius = (value - 32) * 5 / 9 temp = Temperature(98) print(temp._celsius) # 98 temp.fahrenheit = 98.6 print(temp._celsius) # 37.0 # calling the getter print(temp.fahrenheit) # 98.6 temp.fahrenheit = "98F" print(temp._celsius) # TypeError: Value must be an integer
The property decorator, acting as a setter method, is useful especially when we want to:
- Do validation to the value of the attribute.
- Do some pre-processing. before the setting happens.
In the above example, we validated the value of the
_celsius attribute. We then checked if it's not an integer nor float, we raise a
Noe that the name of the decorator (before
.setter) must be identical to the method name. In our case, it's
Also note that the signature of the setter method needs to be
(self, value). The name of the value is arbitrary.
We've seen how to write a getter and a setter method in Python classes using a property decorator. The feature that saves us time and helps write Pythonic and cleaner code.