We first described class data attributes in Section 13.4.1. As a brief reminder, class attributes are simply data values associated with a class and not any par- ticular instances like instance attributes are. Such values are also referred to as static members because their values stay constant, even if a class is invoked due to instantiation multiple times. No matter what, static members maintain their values independent of instances unless explicitly changed. (Comparing instance attributes to class attributes is barely like that of automatic vs. static variables, but this is just a vague analogy . . . do not read too much into it, especially if you are not familiar with auto and static variables.)
Classes and instances are both namespaces. Classes are namespaces for class attributes. Instances are namespaces for instance attributes.
There are a few aspects of class attributes and instance attributes that should be brought to light. The first is that you can access a class attribute with either the class or an instance, provided that the instance does not have an attribute with the same name.
Access to Class Attributes
Class attributes can be accessed via a class or an instance. In the example below, when class C is created with the version class attribute, naturally access is allowed using the class object, i.e., C.version. When instance c is created, access to c.version fails for the instance, and then Python initiates a search for the name version first in the instance, then the class, and then the base classes in the inheritance tree. In this case, it is found in the class:
>>> class C(object): # define class ... version = 1.2 # static member ...
>>> c = C() # instantiation
>>> C.version # access via class 1.2
ptg 538 Chapter 13 Object-Oriented Programming
>>> c.version # access via instance 1.2
>>> C.version += 0.1 # update (only) via class
>>> C.version # class access 1.3
>>> c.version # instance access, which
1.3 # also reflected change
However, we can only update the value when referring to it using the class, as in the C.version increment statement above. Attempting to set or update the class attribute using the instance name will create an instance attribute that “shadows” access to the class attribute, effectively hiding it from scope until or unless that shadow is removed.
Use Caution When Accessing Class Attribute with Instance
Any type of assignment of a local attribute will result in the creation and assignment of an instance attribute, just like a regular Python variable. If a class attribute exists with the same name, interesting side effects can occur.
(This is true for both classic and new-style classes.)
>>> class Foo(object):
... x = 1.5 ...
>>> foo = Foo()
>>> foo.x 1.5
>>> foo.x = 1.7 # try to update class attr
>>> foo.x # looks good so far...
1.7
>>> Foo.x # nope, just created a new inst attr 1.5
In the above code snippet, a new instance attribute named version is created, overriding the reference to the class attribute. However, the class attribute itself is unscathed and still exists in the class domain and can still be accessed as a class attribute, as we can see above. What would happen if we delete this new reference? To find out, we will use the del statement on foo.x.
>>> del foo.x # delete instance attribute
>>> foo.x # can now access class attr again 1.5
ptg 13.6 Instance Attributes 539
So by assigning an instance attribute with the same name as a class attribute, we effectively “hide” the class attribute, but once we remove the instance attribute, we can “see” the class one again. Now let us try to update the class attribute again, but this time, we will just try an innocent increment:
>>> foo.x += .2 # try to increment class attr
>>> foo.x 1.7
>>> Foo.x # nope, same thing 1.5
It is still a “no go.” We again created a new instance attribute while leaving the original class attribute intact. (For those who have or want a deeper understanding of Python: the attribute was already in the class’s dictionary [__dict__]. With the assignment, one is now added to the instance’s __dict__.) The expression on the right-hand side of the assignment evalu- ates the original class variable, adds 0.2 to it, and assigns it to a newly created instance attribute. Note that the following is an equivalent assignment, but it may provide more clarification:
foo.x = Foo.x + 0.2
But… all of this changes if the class attribute is mutable:
>>> class Foo(object):
... x = {2003: 'poe2'}
...
>>> foo = Foo()
>>> foo.x {2003: 'poe2'}
>>> foo.x[2004] = 'valid path'
>>> foo.x
{2003: 'poe2', 2004: 'valid path'}
>>> Foo.x # it works!!!
{2003: 'poe2', 2004: 'valid path'}
>>> del foo.x # no shadow so cannot delete Traceback (most recent call last):
File "<stdin>", line 1, in ? del foo.x
AttributeError: x
>>>
Class Attributes More Persistent
Static members, true to their name, hang around while instances (and their attributes) come and go (hence independent of instances). Also, if a new instance is created after a class attribute has been modified, the updated
ptg 540 Chapter 13 Object-Oriented Programming
value will be reflected. Class attribute changes are reflected across all instances:
>>> class C(object):
... spam = 100 # class attribute ...
>>> c1 = C() # create an instance
>>> c1.spam # access class attr thru inst.
100
>>> C.spam += 100 # update class attribute
>>> C.spam # see change in attribute 200
>>> c1.spam # confirm change in attribute 200
>>> c2 = C() # create another instance
>>> c2.spam # verify class attribute 200
>>> del c1 # remove one instance
>>> C.spam += 200 # update class attribute again
>>> c2.spam # verify that attribute changed 400
CORE TIP: Use a class attribute to modify itself (not an instance attribute)
As we have seen above, it is perilous to try and modify a class attribute by using an instance attribute. The reason is because instances have their own set of attributes, and there is no clear way in Python to indicate that you want to modify the class attribute of the same name, e.g., there is no global keyword like there is when setting a global inside a function (instead of a local variable of the same name). Always modify a class attribute with the class name, not an instance.