DekGenius.com
[ Team LiB ] Previous Section Next Section

21.1 The Class Statement

Although the Python class statement seems similar to other OOP languages on the surface, on closer inspection it is quite different than what some programmers are used to. For example, as in C++, the class statement is Python's main OOP tool. Unlike C++, Python's class is not a declaration. Like def, class is an object builder, and an implicit assignment—when run, it generates a class object, and stores a reference to it in the name used in the header. Also like def, class is true executable code—your class doesn't exist until Python reaches and runs the class statement (typically, while importing the module it is coded in, but not until).

21.1.1 General Form

class is a compound statement with a body of indented statements usually under it. In the header, superclasses are listed in parentheses after the class name, separated by commas. Listing more than one superclass leads to multiple inheritance (which we'll say more about in the next chapter). Here is the statement's general form:

class <name>(superclass,...):       # Assign to name.
    data = value                    # Shared class data
    def method(self,...):           # Methods
        self.member = value         # Per-instance data

Within the class statement, any assignment generates a class attribute, and specially-named methods overload operators; for instance, a function called __init__ is called at instance object construction time, if defined.

21.1.2 Example

Classes are mostly just namespaces—a tool for defining names (i.e., attributes) that export data and logic to clients. So how do you get from the class statement to a namespace?

Here's how. Just as with modules files, the statements nested in a class statement body create its attributes. When Python executes a class statement (not a call to a class), it runs all the statements in its body, from top to bottom. Assignments that happen during this process create names in the class's local scope, which become attributes in the associated class object. Because of this, classes resemble both modules and functions:

  • Like functions, class statements are a local scope where names created by nested assignments live.

  • Like modules, names assigned in a class statement become attributes in a class object.

The main distinction for classes is that their namespaces are also the basis of inheritance in Python; attributes are fetched from other classes if not found in a class or instance object.

Because class is a compound statement, any sort of statement can be nested inside its body—print, =, if, def, and so on. All the statements inside the class statement run when the class statement itself runs (not when the class is later called to make an instance). Any name assigned inside the class statement makes a class attribute. Nested defs make class methods, but other assignments make attributes, too. For example:

>>> class SharedData:
...     spam = 42               # Generates a class attribute.
...
>>> x = SharedData(  )            # Make two instances.
>>> y = SharedData(  )
>>> x.spam, y.spam              # They inherit and share spam.
(42, 42)

Here, because the name spam is assigned at the top-level of a class statement, it is attached to the class, and so will be shared by all instances. Change it by going through the class name; refer to it through either instances or the class.[1]

[1] If you've used C++, you may recognize this as similar to the notion of C++'s "static" class data—members that are stored in the class, independent of instances. In Python, it's nothing special: all class attributes are just names assigned in the class statement, whether they happen to reference functions (C++'s "methods") or something else (C++'s "members").

>>> SharedData.spam = 99
>>> x.spam, y.spam, SharedData.spam
(99, 99, 99)

Such class attributes can be used to manage information that spans all the instances—a counter of the number of instances generated, for example (we'll expand on this idea in Chapter 23). Now, watch what happens if we assign name spam through an instance instead of the class:

>>> x.spam = 88
>>> x.spam, y.spam, SharedData.spam
(88, 99, 99)

Assignments to instance attributes create or change that name in the instance, rather than the shared class. More generally, inheritance search occurs only on attribute reference, not assignment: assigning to an object's attribute always changes that object, and no other.[2] For example, y.spam is looked up in the class by inheritance, but the assignment to x.spam attaches a name to x itself.

[2] Unless the attribute assignment operation has been redefined by a class with the __setattr__ operator overloading method to do something unique.

Here's a more comprehensive example of this behavior, that stores the same name in two places. Suppose we run the following class:

class MixedNames:                          # Define class.
    data = 'spam'                          # Assign class attr.
    def __init__(self, value):              # Assign method name.
        self.data = value                  # Assign instance attr.
    def display(self):
        print self.data, MixedNames.data    # Instance attr, class attr

This class contains two defs, which bind class attributes to method functions. It also contains an = assignment statement; since this class-level assignment assigns the name data inside the class, it lives in the class's local scope and becomes an attribute of the class object. Like all class attributes, this data is inherited and shared by all instances of the class that don't have a data of their own.

When we make instances of this class, the name data is also attached to instances, by the assignment to self.data in the constructor method:

>>> x = MixedNames(1)           # Make two instance objects.
>>> y = MixedNames(2)           # Each has its own data.
>>> x.display(  ); y.display(  )     # self.data differs, Subclass.data same.
1 spam
2 spam

The net result is that data lives in two places: in instance objects (created by the self.data assignment in __init__), and in the class they inherit names from (created by the data assignment in the class). The class's display method prints both versions, by first qualifying the self instance, and then the class.

By using these techniques to store attributes on different objects, you determine their scope of visibility. When attached to classes, names are shared; in instances, names record per-instance data, not shared behavior or data. Although inheritance looks up names for us, we can always get to an attribute anywhere in a tree, by accessing the desired object directly.

In the example, x.data and self.data will choose an instance name, which normally hides the same name in the class. But MixedNames.data grabs the class name explicitly. We'll see various roles for such coding patterns later; the next section describes one of the most common.

    [ Team LiB ] Previous Section Next Section