This is the long version of the attribute access story, included just for the sake of completeness.
When retrieving an attribute from
an object (print objectname.attrname) Python follows
these steps:
If
attrnameis a Python-provided attribute forobjectname, return it.Check
objectname.__class__.__dict__forattrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases ofobjectname.__class__for the same case.Check
objectname.__dict__forattrname, and return if found. Ifobjectnameis a type object, search its bases too. If it is a type object and a descriptor is found in the object or its bases, return the descriptor result.Check
objectname.__class__.__dict__forattrname. If it exists and is a non-data descriptor, return the descriptor result. If it exists, and is not a descriptor, just return it. If it exists and is a data descriptor, we shouldn't be here because we would have returned at point 2. Search all bases ofobjectname.__class__for same case.Raise
AttributeError
Note that Python first checks for a data
descriptor in the type (and its bases), then for the attribute in the
object __dict__, and then for a
non-data descriptor in the type (and its
bases). These are points 2, 3 and 4 above.
The descriptor result above implies
the result of calling the __get__() method of the
descriptor with appropriate arguments. Also, checking a
__dict__ for attrname means
checking if __dict__["attrname"] exists.
Now, the steps Python follows when setting
a user-defined attribute (objectname.attrname =
something):
Check
objectname.__class__.__dict__forattrname. If it exists and is a data-descriptor, use the descriptor to set the value. Search all bases ofobjectname.__class__for the same case.Insert
somethingintoobjectname.__dict__for key"attrname".Think "Wow, this was much simpler!"
What happens when setting a Python-provided attribute depends on the attribute. Python may not even allow some attributes to be set. Deletion of attributes is very similar to setting as above.