Chapter 1. New Attribute Access

The Dynamic __dict__

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"  1
...
>>> cobj = C()
>>> cobj.instattr = "attr on instance"  2
>>>
>>> cobj.instattr  3
'attr on instance'
>>> cobj.classattr 4
'attr on class'
>>> C.__dict__['classattr'] 5
'attr on class'
>>> cobj.__dict__['instattr'] 6
'attr on instance'
>>>
>>> cobj.__dict__ 7
{'instattr': 'attr on instance'}
>>> C.__dict__ 8
{'classattr': 'attr on class', '__module__': '__main__', '__doc__': None}


1

Attributes can be set on a class.

2

Or even on an instance of a class.

3 4

Both, class and instance attributes are accessible from an instance.

5 6

Attributes really sit inside a dictionary-like __dict__ in the object.

7 8

__dict__ contains only the user-provided attributes.

Ok, I admit 'user-provided attribute' is a term I made up, but I think it is useful to understand what is going on. Note that __dict__ is itself an attribute. We didn't set this attribute ourselves, but Python provides it. 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 classes).

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 (for e.g. print objectname.attributename), the following objects are searched in sequence for the attribute:

  1. The object itself (objectname.__dict__ or any Python-provided attribute of objectname).

  2. The object's type (objectname.__class__.__dict__). Observe that only __dict__ is searched, which means only user-provided attributes of the class. In other words objectname.__bases__ may not return anything even though objectname.__class__.__bases__ does exist.

  3. The bases of the object's class, their bases, and so on. (__dict__ of each of objectname.__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. Also look at the inspect module in the standard library for more functions to inspect objects.

The above section explains the general mechanism for all objects. Even for classes (for example accessing classname.attrname), with a slight modification: the bases of the class are searched before the class of the class (which is classname.__class__ and 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.