Skip to content
All Posts

Multiple Inheritance in Python

An exploration of Python multiple inheritance through MRO, C3 linearization, diamond hierarchies, and super calls.

Multiple inheritance in Python raises questions about lookup order—the method resolution order, or MRO—and repeated calls in diamond inheritance.

MRO

The method resolution order determines which parent class supplies an attribute requested by a child class. Before Python 2.3, the MRO used depth-first search. Python 2.3 introduced the C3 algorithm for classes that inherit from object. These are called new-style classes; classes that do not inherit from object are old-style classes.

Comparison of method lookup orders in Python

The diagram shows old-style classes using depth-first lookup and new-style classes using a breadth-oriented order. C3 was originally proposed for Lisp. Python adopted it because depth-first lookup did not preserve local precedence or monotonicity.

  • Local precedence: The order in which parent classes are declared matters. For C(A, B), lookup should inspect A before B.
  • Monotonicity: If A precedes B in the resolution order for C, the same order must hold in every subclass of C.

Example

class X(object):
def f(self):
print 'x'
class A(X):
def f(self):
print 'a'
def extral(self):
print 'extral a'
class B(X):
def f(self):
print 'b'
def extral(self):
print 'extral b'
class C(A, B, X):
def f(self):
super(C, self).f()
print 'c'
print C.mro()
c = C()
c.f()
c.extral()

The lookup reaches A first, so the result follows directly.

The original result image has been removed from the source site: original image URL

Class C does not define extral, so it calls the inherited method. This pattern, in which part of a class’s behavior is supplied by a parent, is sometimes described as an abstract superclass.

About super

The MRO makes clear that super refers to the next class in the MRO, not simply to a parent class. Conceptually, it behaves like:

def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]

When calling parent behavior from a subclass, either call the parent class explicitly or use super consistently. It is best not to mix the two styles.


Originally published on SegmentFault.