Online Book Reader

Home Category

Learning Python - Mark Lutz [427]

By Root 1770 0
the inheritance search procedure is based upon the assumption that if you mix in C lower in the tree, you probably intend to grab its attributes in preference to A’s. It also assumes that C is always intended to override A’s attributes in all contexts, which is probably true when it’s used standalone but may not be when it’s mixed into a diamond with classic classes—you might not even know that C may be mixed in like this when you code it.

Since it is most likely that the programmer meant that C should override A in this case, though, new-style classes visit C first. Otherwise, C could be essentially pointless in a diamond context: it could not customize A and would be used only for names unique to C.

Explicit conflict resolution

Of course, the problem with assumptions is that they assume things. If this search order deviation seems too subtle to remember, or if you want more control over the search process, you can always force the selection of an attribute from anywhere in the tree by assigning or otherwise naming the one you want at the place where the classes are mixed together:

>>> class A:

attr = 1 # Classic

>>> class B(A):

pass

>>> class C(A):

attr = 2

>>> class D(B, C):

attr = C.attr # Choose C, to the right

>>> x = D()

>>> x.attr # Works like new-style (all 3.0)

2

Here, a tree of classic classes is emulating the search order of new-style classes: the assignment to the attribute in D picks the version in C, thereby subverting the normal inheritance search path (D.attr will be lowest in the tree). New-style classes can similarly emulate classic classes by choosing the attribute above at the place where the classes are mixed together:

>>> class A(object):

attr = 1 # New-style

>>> class B(A):

pass

>>> class C(A):

attr = 2

>>> class D(B, C):

attr = B.attr # Choose A.attr, above

>>> x = D()

>>> x.attr # Works like classic (default 2.6)

1

If you are willing to always resolve conflicts like this, you can largely ignore the search order difference and not rely on assumptions about what you meant when you coded your classes.

Naturally, attributes picked this way can also be method functions—methods are normal, assignable objects:

>>> class A:

def meth(s): print('A.meth')

>>> class C(A):

def meth(s): print('C.meth')

>>> class B(A):

pass

>>> class D(B, C): pass # Use default search order

>>> x = D() # Will vary per class type

>>> x.meth() # Defaults to classic order in 2.6

A.meth

>>> class D(B, C): meth = C.meth # Pick C's method: new-style (and 3.0)

>>> x = D()

>>> x.meth()

C.meth

>>> class D(B, C): meth = B.meth # Pick B's method: classic

>>> x = D()

>>> x.meth()

A.meth

Here, we select methods by explicitly assigning to names lower in the tree. We might also simply call the desired class explicitly; in practice, this pattern might be more common, especially for things like constructors:

class D(B, C):

def meth(self): # Redefine lower

...

C.meth(self) # Pick C's method by calling

Such selections by assignment or call at mix-in points can effectively insulate your code from this difference in class flavors. Explicitly resolving the conflicts this way ensures that your code won’t vary per Python version in the future (apart from perhaps needing to derive classes from object or a built-in type for the new-style tools in 2.6).

* * *

Note


Even without the classic/new-style class divergence, the explicit method resolution technique shown here may come in handy in multiple inheritance scenarios in general. For instance, if you want part of a superclass on the left and part of a superclass on the right, you might need to tell Python which same-named attributes to choose by using explicit assignments in subclasses. We’ll revisit this notion in a “gotcha” at the end of this chapter.

Also note that diamond inheritance patterns might be more problematic in some cases than I’ve implied here (e.g., what if B and C both have required constructors that call to the constructor in A?). Since such contexts are rare in real-world Python, we

Return Main Page Previous Page Next Page

®Online Book Reader