Link Search Menu Expand Document

Setter decorator

Object-oriented programming helps us to protect the attributes of our class from unwanted modifications. This follows the principle of encapsulation. Just as the engine of a car is hidden from the outside to protect it, Python allows us to protect the attributes of our classes.

Imagine that you want to protect the age attribute so that a negative value cannot be used. One way to do this is to check the age in the constructor.

class Person:
    def __init__(self, first_name, last_name, age):
        if age < 0:
            raise Exception("Age cannot be negative.")
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

This protects against the following:

p = Person("Ana", "Ruiz", -1)
# Exception: Age cannot be negative.

However, we are not protected from the following. We build the object with a valid age, but then modify it.

p = Person("Ana", "Ruiz", 20)
p.age = -1
print(p.age) # -1

To solve this, we can use the following decorators, which go hand in hand:

  • age.setter: Allows adding logic to be executed when the attribute is modified, in this case age.
  • property: Allows access to an attribute. We will see it in detail later.
class Person:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if age < 0:
            raise ValueError("Age cannot be negative.")
        self.__age = age

Some notes:

  • By using the __ in __age, we tell Python that we want to hide this attribute from the outside. It is like saying we do not want anyone to modify it. It is internal to the class.
  • With @age.setter, we tell Python that anyone who wants to modify age must do it through this method. And this method has some logic. We forbid the age to be negative.

As you can see, we are now protected from a negative age.

p = Person("Ana", "Ruiz", 20)
p.age = -1
# ValueError: Age cannot be negative.

This is an example of the famous encapsulation of object-oriented programming. It encapsulates the attributes by isolating them from the outside world and can only be modified by certain functions, which are responsible for verifying that everything is correct.

An important note is that Python allows you to do everything. If you really try and want to skip the verification, you can set a negative age using _Person__age. But this is a bit more advanced and a field you should not access.

p = Person("Ana", "Ruiz", 20)
p._Person__age = -1
print(p.age) # -1