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.