Learning Python - Mark Lutz [413]
Multiple Inheritance: “Mix-in” Classes
Many class-based designs call for combining disparate sets of methods. In a class statement, more than one superclass can be listed in parentheses in the header line. When you do this, you use something called multiple inheritance—the class and its instances inherit names from all the listed superclasses.
When searching for an attribute, Python’s inheritance search traverses all superclasses in the class header from left to right until a match is found. Technically, because any of the superclasses may have superclasses of its own, this search can be a bit more complex for larger class tress:
In classic classes (the default until Python 3.0), the attribute search proceeds depth-first all the way to the top of the inheritance tree, and then from left to right.
In new-style classes (and all classes in 3.0), the attribute search proceeds across by tree levels, in a more breadth-first fashion (see the new-style class discussion in the next chapter).
In either model, though, when a class has multiple superclasses, they are searched from left to right according to the order listed in the class statement header lines.
In general, multiple inheritance is good for modeling objects that belong to more than one set. For instance, a person may be an engineer, a writer, a musician, and so on, and inherit properties from all such sets. With multiple inheritance, objects obtain the union of the behavior in all their superclasses.
Perhaps the most common way multiple inheritance is used is to “mix in” general-purpose methods from superclasses. Such superclasses are usually called mix-in classes—they provide methods you add to application classes by inheritance. In a sense, mix-in classes are similar to modules: they provide packages of methods for use in their client subclasses. Unlike simple functions in modules, though, methods in mix-ins also have access to the self instance, for using state information and other methods. The next section demonstrates one common use case for such tools.
Coding Mix-in Display Classes
As we’ve seen, Python’s default way to print a class instance object isn’t incredibly useful:
>>> class Spam:
... def __init__(self): # No __repr__ or __str__
... self.data1 = "food"
...
>>> X = Spam()
>>> print(X) # Default: class, address
<__main__.Spam object at 0x00864818> # Displays "instance" in Python 2.6
As you saw in Chapter 29 when studying operator overloading, you can provide a __str__ or __repr__ method to implement a custom string representation of your own. But, rather than coding one of these in each and every class you wish to print, why not code it once in a general-purpose tool class and inherit it in all your classes?
That’s what mix-ins are for. Defining a display method in a mix-in superclass once enables us to reuse it anywhere we want to see a custom display format. We’ve already seen tools that do related work:
Chapter 27’s AttrDisplay class formatted instance attributes in a generic __str__ method, but it did not climb class trees and was used in single-inheritance mode only.
Chapter 28’s classtree.py module defined functions for climbing and sketching class trees, but it did not display object attributes along the way and was not architected as an inheritable class.
Here, we’re going to revisit these examples’ techniques and expand upon them to code a set of three mix-in classes that serve as generic display tools for listing instance attributes, inherited attributes, and attributes on all objects in a class tree. We’ll also use our tools in multiple-inheritance mode and deploy coding techniques that make classes better suited to use as generic tools.
Listing instance attributes with __dict__
Let’s get started with the simple case—listing attributes attached to an instance. The