What is an attribute? Quite simply, an attribute is a way to get from
one object to another. Apply the power of the almighty dot -
objectname.attributename - and voila! you now have
the handle to another object. You also have the power to create
attributes, by assignment: objectname.attributename =
notherobject.
Which object does an attribute access return, though? And where does the object set as an attribute end up? These questions are answered in this chapter.
Example 1.1. Simple attribute access
>>> class C(object): ... classattr = "attr on class"... >>> cobj = C() >>> cobj.instattr = "attr on instance"
>>> >>> cobj.instattr
'attr on instance' >>> cobj.classattr
'attr on class' >>> C.__dict__['classattr']
'attr on class' >>> cobj.__dict__['instattr']
'attr on instance' >>> >>> cobj.__dict__
{'instattr': 'attr on instance'} >>> C.__dict__
{'classattr': 'attr on class', '__module__': '__main__', '__doc__': None}
Note that __dict__ is itself an attribute. We
didn't set this attribute ourselves, but Python did. Our old friends
__class__ and __bases__ (none
which appear to be in __dict__ either) also seem to
be similar. Let's call them Python-provided
attributes. Whether an attribute is Python-provided or not depends on
the object in question (__bases__, for example, is
Python-provided only for type objects).
We, however, are more interested in user-defined
attributes. These are attributes provided by the user, and they
usually (but not always) end up in the __dict__ of
the object on which they're set.
When accessed (eg.print
objectname.attributename), the following objects are
searched in sequence for the attribute:
The object itself (
objectname.__dict__or any Python-provided attribute ofobjectname).The object's type (
objectname.__class__.__dict__). Observe that only__dict__is searched, which means only user-provided attributes of the type. In other wordsobjectname.__bases__may not return anything even thoughobjectname.__class__.__bases__does exist.The bases of the object's type, their bases, and so on. (
__dict__of each ofobjectname.__class__.__bases__). More than one base does not confuse Python, and should not concern us at the moment. The point to note is that all bases are searched until an attribute is found.
If all this hunting around fails to find a suitably named attribute,
Python raises an AttributeError. The type of the
type (objectname.__class__.__class__) is never
searched for attribute access on an object
(objectname in the example).
The built-in dir() function returns a list of
all attributes of an object.
The above section explains the general mechanism for
all objects. Even for type objects (for example
accessing classname.attrname), with a slight
modification: the bases of the type object are searched before the
type of the type object (which is
classname.__class__, which for most types, by the
way, is <type 'type'>).
Some objects, such as built-in types and their instances (lists,
tuples, etc.) do not have a __dict__. Consequently
user-defined attributes cannot be set on them.
We're not done yet! This was the short version of the story. There is more to what can happen when setting and getting attributes. This is explored in the following sections.
...
>>> cobj = C()
>>> cobj.instattr = "attr on instance"
>>>
>>> cobj.instattr
'attr on instance'
>>> cobj.classattr
'attr on class'
>>> C.__dict__['classattr']
'attr on class'
>>> cobj.__dict__['instattr']
'attr on instance'
>>>
>>> cobj.__dict__
{'instattr': 'attr on instance'}
>>> C.__dict__
{'classattr': 'attr on class', '__module__': '__main__', '__doc__': None}