How to Supercharge Python Classes With Property

Created on Feb 25, 2022

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 get_ and 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.

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius

    def get_fahrenheit(self):
        return (self._celsius * 9 / 5) + 32

temperature = Temperature(37)
print(temperature.get_fahrenheit())
# 98.6

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:

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius

    @property
    def fahrenheit(self):
        return (self._celsius * 9 / 5) + 32

temperature = Temperature(37)
print(temperature.fahrenheit)
# 98.6

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 (self) .

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:

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius

    def get_fahrenheit(self):
        return (self._celsius * 9 / 5) + 32
    
    fahrenhei = property(fget=get_fahrenheit)

temperature = Temperature(37)
print(temperature.fahrenhei)
# 98.6

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 _celsius attribute:

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:

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 TypeError .

Noe that the name of the decorator (before .setter ) must be identical to the method name. In our case, it’s fahrenheit .

Also note that the signature of the setter method needs to be (self, value) . The name of the value is arbitrary.

Conclusion

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.