user3103957
user3103957

Reputation: 848

Python __set__() descriptor behaviour

I have the following Python code snippet.

class LoggedAgeAccess:

    def __get__(self, obj, objtype=None):
        value = obj._age
        return value

    def __set__(self, obj, value):
        obj._age = value

class Person:

    age = LoggedAgeAccess()             # Descriptor instance

    def __init__(self, name, age):
        self.name = name                # Regular instance attribute
        self.age = age                  # Calls __set__()

    def birthday(self):
        self.age += 1                   # Calls both __get__() and __set__()

x = Person("ABC", 27)

In the constructor, the second assignment statement self.age = age triggers the __set__() method of the LoggedAgeAccess descriptor. This is really confusing.

When the Person object is created, I am passing name and age values. Inside the constructor, I refer the passed value and create a instance specific variable self.age whose value is assigned with the passed value (27). How this assignment statement refers the class level age variable and triggers a call to __set__()?

Upvotes: 0

Views: 52

Answers (1)

chepner
chepner

Reputation: 532093

The key is to recognize that self.age = age does not unconditionally create an instance attribute named age on the object self. Python first checks if self.__dict__['age'] already exists (it does not), if Person.age exists (it does) and if Person.age has a __set__ method (it does), and so invokes Person.age.__set__(self, age) instead of creating an instance attribute.

If self.__dict__['age'] does exist, the value is updated. If Person.age was not already defined, then a new instance attribute named age is created. If Person.age does exists, but does not have a __set__ method, then a new instance attribute is also created, and that attribute shadows the class attribute with the same name.

Upvotes: 2

Related Questions