DekGenius.com
[ Team LiB ] Previous Section Next Section

21.3 Inheritance

The whole point of a namespace tool like the class statement is to support name inheritance. This section expands on some of the mechanisms and roles of attribute inheritance.

In Python, inheritance happens when an object is qualified, and involves searching an attribute definition tree (one or more namespaces). Every time you use an expression of the form object.attr where object is an instance or class object, Python searches the namespace tree at and above object, for the first attr it can find. This includes references to self attributes in your methods. Because lower definitions in the tree override higher ones, inheritance forms the basis of specialization.

21.3.1 Attribute Tree Construction

Figure 21-1 summarizes the way namespace trees are constructed and populated with names. Generally:

  • Instance attributes are generated by assignments to self attributes in methods.

  • Class attributes are created by statements (assignments) in class statements.

  • Superclass links are made by listing classes in parentheses in a class statement header.

Figure 21-1. Namespaces tree construction and inheritance
figs/lpy2_2101.gif

The net result is a tree of attribute namespaces, which grows from an instance, to the class it was generated from, to all the superclasses listed in the class headers. Python searches upward in this tree from instances to superclasses, each time you use qualification to fetch an attribute name from an instance object.[4]

[4] This description isn't 100% complete, because instance and class attributes can also be created by assigning to objects outside class statements. But that's much less common and sometimes more error prone (changes aren't isolated to class statements). In Python all attributes are always accessible by default; we talk more about name privacy in Chapter 23.

21.3.2 Specializing Inherited Methods

The tree-searching model of inheritance just described turns out to be a great way to specialize systems. Because inheritance finds names in subclasses before it checks superclasses, subclasses can replace default behavior by redefining the superclass's attributes. In fact, you can build entire systems as hierarchies of classes, which are extended by adding new external subclasses rather than changing existing logic in place.

The idea of redefining inherited names leads to a variety of specialization techniques. For instance, subclasses may replace inherited attributes completely, provide attributes that a superclass expects to find, and extend superclass methods by calling back to the superclass from an overridden method. We've already seen replacement in action; here's an example that shows how extension works:

>>> class Super:
...     def method(self):
...         print 'in Super.method'
... 
>>> class Sub(Super):
...     def method(self):                       # Override method.
...         print 'starting Sub.method'         # Add actions here.
...         Super.method(self)                  # Run default action.
...         print 'ending Sub.method'
...

Direct superclass method calls are the crux of the matter here. The Sub class replaces Super's method function with its own specialized version. But within the replacement, Sub calls back to the version exported by Super to carry out the default behavior. In other words, Sub.method just extends Super.method behavior, rather than replacing it completely:

>>> x = Super(  )            # Make a Super instance.
>>> x.method(  )             # Runs Super.method
in Super.method

>>> x = Sub(  )              # Make a Sub instance.
>>> x.method(  )             # Runs Sub.method, which calls Super.method
starting Sub.method
in Super.method
ending Sub.method

This extension coding pattern is also commonly used with constructors; see Section 21.2 for an example.

21.3.3 Class Interface Techniques

Extension is only one way to interface with a superclass; the following file, specialize.py, defines multiple classes that illustrate a variety of common techniques:


Super

Defines a method function and a delegate that expects an action in a subclass


Inheritor

Doesn't provide any new names, so it gets everything defined in Super


Replacer

Overrides Super's method with a version of its own


Extender

Customizes Super's method by overriding and calling back to run the default


Provider

Implements the action method expected by Super's delegate method

Study each of these subclasses to get a feel for the various ways they customize their common superclass:

class Super:
    def method(self):
        print 'in Super.method'       # Default behavior
    def delegate(self):
        self.action(  )                    # Expected to be defined

class Inheritor(Super):               # Inherit method verbatim.
    pass

class Replacer(Super):                # Replace method completely.
    def method(self):
        print 'in Replacer.method'

class Extender(Super):                # Extend method behavior.
    def method(self):
        print 'starting Extender.method'
        Super.method(self)
        print 'ending Extender.method'

class Provider(Super):                # Fill in a required method.
    def action(self):
        print 'in Provider.action'

if __name__ == '__main__':
    for klass in (Inheritor, Replacer, Extender):
        print '\n' + klass.__name__ + '...'
        klass(  ).method(  )

    print '\nProvider...'
    x = Provider(  )
    x.delegate(  )

A few things are worth pointing out here. The self-test code at the end of this example creates instances of three different classes in a for loop. Because classes are objects, you can put them in a tuple and create instances generically (more on this idea later). Classes also have the special __name__ attribute like modules; it's just preset to a string containing the name in the class header.

% python specialize.py

Inheritor...
in Super.method

Replacer...
in Replacer.method

Extender...
starting Extender.method
in Super.method
ending Extender.method

Provider...
in Provider.action

21.3.4 Abstract Superclasses

Notice how the Provider class in the prior example works. When we call the delegate method through a Provider instance, two independent inheritance searches occur:

  1. On the initial x.delegate call, Python finds the delegate method in Super, by searching at the Provider instance and above. The instance x is passed into the method's self argument as usual.

  2. Inside the Super.delegate method, self.action invokes a new, independent inheritance search at self and above. Because self references a Provider instance, the action method is located in the Provider subclass.

This "filling in the blanks" sort of coding structure is typical of OOP frameworks. At least in terms of the delegate method, the superclass in this example is what is sometimes called an abstract superclass—a class that expects parts of its behavior to be provided by subclasses. If an expected method is not defined in a subclass, Python raises an undefined name exception after inheritance search fails. Class coders sometimes make such subclass requirements more obvious with assert statements, or raising the built-in NotImplementedError exception:

class Super:
    def method(self):
        print 'in Super.method'
    def delegate(self):
        self.action(  )
    def action(self):
        assert 0, 'action must be defined!'

We'll meet assert in Chapter 24; in short, if its expression evaluates to false, it raises an exception with an error message. Here, the expression is always false (0), so as to trigger an error message if a method is not redefined and inheritance locates the version here. Alternatively, some classes simply raise a NotImplemented exception directly in such method stubs; we'll study the raise statement in Chapter 24.

For a somewhat more realistic example of this section's concepts in action, see the "Zoo Animal Hierarchy" exercise in Part VI Exercises and its solution in Section B.6. Such taxonomies are a traditional way to introduce OOP, but are a bit removed from most developers' job descriptions.

    [ Team LiB ] Previous Section Next Section