[ Team LiB ] |
21.3 InheritanceThe 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 ConstructionFigure 21-1 summarizes the way namespace trees are constructed and populated with names. Generally:
Figure 21-1. Namespaces tree construction and inheritanceThe 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]
21.3.2 Specializing Inherited MethodsThe 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 TechniquesExtension is only one way to interface with a superclass; the following file, specialize.py, defines multiple classes that illustrate a variety of common techniques:
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 SuperclassesNotice how the Provider class in the prior example works. When we call the delegate method through a Provider instance, two independent inheritance searches occur:
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 ] |