Link Search Menu Expand Document

Property decorator

Previously, we defined the attribute age for our Person. However, age changes. In 5 years, you are not the same age.

To be consistent, we would have to continuously modify the age, and this is impractical. It is better to store the date of birth.

class Person:
    def __init__(self, first_name, last_name, birth_date):
        self.first_name = first_name
        self.last_name = last_name
        self.birth_date = birth_date

With the date of birth, we can calculate the age. It is as simple as looking at the time that has passed between birth_date and the current time.

We can then define an age method that returns the age, calculated from the date of birth. Much better.

from datetime import datetime

class Person:
    # ...
    def age(self):
        today = datetime.now()
        age = today.year - self.birth_date.year

        if (today.month, today.day) < (self.birth_date.month, self.birth_date.day):
            age -= 1

        return age

We can now access the age through the method we have created. We no longer need to update the age as time passes. It is calculated automatically.

p = Person("Juan", "Prieto", datetime(1993, 11, 10))
print(p.age())

The example is perfectly valid, but now we have age() (a method) instead of what we had before age (an attribute). It can then be confusing because now first_name and age are accessed in different ways. Conceptually, both are attributes.

print(p.first_name)
print(p.age())

Precisely for this, we have the @property decorator. It is very Pythonic and allows using age instead of age().

class Person:
    # ...
    @property
    def age(self):
        today = datetime.now()
        age = today.year - self.birth_date.year

        if (today.month, today.day) < (self.birth_date.month, self.birth_date.day):
            age -= 1

        return age

Now age can be accessed as if it were an attribute, without the () seen above.

print(p.first_name)
print(p.age)

But age is a bit of a special attribute. Its value is not stored as such. It is calculated each time using the date of birth.

Having seen this, we can now understand the different types of attributes with respect to the way they are evaluated. These can be:

  • 🏎️ Eager: The first_name is stored β€œas is” in memory. No computation is needed to return it. It is the fastest.
  • 🐌 Lazy: The age is not stored. It is calculated when you want to access it, using the date of birth. This requires calculations. It can be slower depending on the complexity of the calculations. It is usually associated with @property.

A few notes to decide the type of evaluation, eager or lazy:

  • In cases like first_name, it is clear that we want eager.
  • With age, it is clear that we want lazy, using the date of birth.
  • In other cases, it depends. Using lazy with an attribute that requires a lot of calculations and that there are thousands of processes accessing it hundreds of times per second might be a bad idea, as it will be continuously calculating the attribute.