One of the things that surprised me recently is that the Python
language reference doesn't seem to
document the specifics of how Python searches for the attribute when you
try to access
obj.attr. So here is a not completely formal description
of the process for Python 2.7 or so for new-style classes.
(Such a description really belongs in the language reference because
it's an important part of Python's semantics. It's possible that you
can reassemble a full description of the lookup semantics from the bits
and pieces scattered throughout the language reference.)
First, any time Python looks for an attribute on an object it
doesn't just search the object's (nominal) attribute dictionary; it
also searches back through the object's base classes (in method
resolution order), ultimately terminating at
object. Ordinary instances of classes don't have bases (and can't be
given them), but classes do.
Let's say we're looking for
attr on the object
the object's type object (ie
type(obj)). Then the lookup order is
more or less:
typ.attr exists and has both a
__get__ and either a
__delete__ attribute, it's a data descriptor;
we call it and return the result.
(Data descriptors thus deliberately preempt attributes on the object
- search for
obj.attr. On classes (but not on non-class instances)
this may be a descriptor with a
__get__ instead of a plain
attribute; if it is, it's called.
(Classes evaluating their own descriptors is necessary in order
to make various descriptor and property things work when you
directly access the class. Consider
- search for
typ.attr. This may be a plain attribute or a non-data
descriptor with a
__get__; if it's a descriptor, it's called.
(As you might guess, the code does not search for
it saves the result of the step one search for use in step three.)
Note that Python never looks at the type of
typ; one level into the
type hierarchy is as far as it goes. By contrast it will go as far along
the base class inheritance hierarchy as it needs to, all the way to
object if necessary.
(This explains how metaclasses can be used to add class-only
attributes on classes.
instance.attr will search
only the instance and
cls, its class, but not go the next step to
type(cls), the metaclass.
cls.attr will search
cls and its base
classes (in step 2) and then the metaclass (in step 3).)
It's important here to not confuse an object's base classes with its
type or its type's base classes. I tend to think of base classes and
types as creating a two dimensional structure, where inheritance goes
sideways through a class's base classes to end up at
its type goes up to end up at
type. Although we often informally
talk about instances of an ordinary class inheriting from things and
attributes being looked up through them, this is not what is actually
happening. A plain instance has no base classes; it is its type (the
class it is an instance of) that has the base classes. Normally making
this distinction is unimportant, but here it's vital.
(The confusing case is a metaclass, which has
type as both its base
class (since metaclasses subclass
type) and its type (since all
classes, metaclasses included, are normally instances of
Sidebar: where this is done in CPython's code
In CPython, this is done in two separate pieces of code. Lookups
for classes are handled by
Objects/typeobject.c. Lookups for non-class objects are handled by
PyObject_GenericGetAttrWithDict(), in Objects/object.c
(which is called with no dictionary argument in this case).