I would like to understand how the built-in function property
works. What confuses me is that property
can also be used as a decorator, but it only
Below is another example on how @property
can help when one has to refactor code which is taken from here (I only summarize it below):
Imagine you created a class Money
like this:
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
and an user creates a library depending on this class where he/she uses e.g.
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
Now let's suppose you decide to change your Money
class and get rid of the dollars
and cents
attributes but instead decide to only track the total amount of cents:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
If the above mentioned user now tries to run his/her library as before
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
it will result in an error
AttributeError: 'Money' object has no attribute 'dollars'
That means that now everyone who relies on your original Money
class would have to change all lines of code where dollars
and cents
are used which can be very painful... So, how could this be avoided? By using @property
!
That is how:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
@property
def dollars(self):
return self.total_cents // 100
@dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
@property
def cents(self):
return self.total_cents % 100
@cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
when we now call from our library
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
it will work as expected and we did not have to change a single line of code in our library! In fact, we would not even have to know that the library we depend on changed.
Also the setter
works fine:
money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.
money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.
You can use @property
also in abstract classes; I give a minimal example here.