From Python in a Nutshell
Getting an attribute from an instance
When you use the syntax
x.name
to refer to an attribute of instancex
of classC
, the lookup proceeds in three steps:
When
name
is found inC
(or in one ofC
’s ancestor classes) as the name of an overriding descriptorv
(i.e.,type(v)
supplies methods__get__
and__set__
)• The value of
x.name
is the result oftype(v).__get__(v, x, C)
Otherwise, when
name
is a key inx.__dict__
•
x.name
fetches and returns the value atx.__dict__['name']
Otherwise,
x.name
delegates the lookup tox
’s class (according to the same two-step lookup used forC.name
, as just detailed)• When a descriptor
v
is found, the overall result of the attribute lookup is, again,type(v).__get__(v, x, C)
• When a nondescriptor value
v
is found, the overall result of the attribute lookup is justv
When these lookup steps do not find an attribute, Python raises an
AttributeError
exception. However, for lookups ofx.name
, whenC
defines or inherits the special method__getattr__
, Python callsC.__getattr__(x,'name')
rather than raising the exception. It’s then up to__getattr__
to either return a suitable value or raise the appropriate exception, normallyAttributeError
.
Are step 1 and the first part of step 3 the same? If yes, why does the same step appear twice?
Do they both happen "when
name
is found inC
(or in one ofC
’s ancestor classes) as the name of an overriding descriptorv
"?
__getattribute__(self, name)
At every request to access attribute
x.y
, Python callsx.__getattribute__('y')
, which must get and return the attribute value or else raiseAttributeError
. The normal semantics of attribute access (usingx.__dict__
,C.__slots__
,C
’s class attributes,x.__getattr__
) are all due toobject.__getattribute__
. When classC
overrides__getattribute__
, it must implement all of the attribute access semantics it wants to offer. Most often, the most convenient way to implement attribute access semantics is by delegating (e.g., callingobject.__getattribute__(self, ...)
as part of the operation of your override of__getattribute__
).