How does the @property decorator work in Python?

后端 未结 13 2132
闹比i
闹比i 2020-11-21 04:49

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

13条回答
  •  时光取名叫无心
    2020-11-21 05:47

    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.

提交回复
热议问题