Python multiple inheritance __init__ not called

Either approach ("new style" or "old style") will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.

Source code accessible: Correct use of "new style"

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

Here, method resolution order (MRO) dictates the following:

  • C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
  • super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
  • super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.

You could say that this case is designed for multiple inheritance.

Source code accessible: Correct use of "old style"

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.

Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.


Now, what if A and B are from a third party library - i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger's article on super - especially the section, "How to Incorporate a Non-cooperative Class").

Third-party parents: A does not implement super; B does

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.

And what if it's the other way around?

Third-party parents: A implements super; B does not

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.

So it seems that unless I know/control the init's of the classes I inherit from (A and B) I cannot make a safe choice for the class I'm writing (C).

Although you can handle the cases where you don't control the source code of A and B by using an adapter class, it is true that you must know how the init's of the parent classes implement super (if at all) in order to do so.

Python supports inheritance from multiple classes. In this lesson, you’ll see:

  • How multiple inheritance works
  • How to use super() to call methods inherited from multiple parents
  • What complexities derive from multiple inheritance
  • How to write a mixin, which is a common use of multiple inheritance

A class can inherit from multiple parents. For example, you could build a class representing a 3D shape by inheriting from two 2D shapes:

class RightPyramid(Triangle, Square):
    def __init__(self, base, slant_height):
        self.base = base
        self.slant_height = slant_height

    def what_am_i(self):
        return 'RightPyramid'

The Method Resolution Order (MRO) determines where Python looks for a method when there is a hierarchy of classes. Using super() accesses the next class in the MRO:

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B(A):
    def __init__(self):
        print('B')
        super().__init__()

class X:
    def __init__(self):
        print('X')
        super().__init__()

class Forward(B, X):
    def __init__(self):
        print('Forward')
        super().__init__()

class Backward(X, B):
    def __init__(self):
        print('Backward')
        super().__init__()

If you combine the MRO and the **kwargs feature for specifying name-value pairs during construction, you can write code that passes parameters to parent classes even if they have different names:

class Rectangle:
    def __init__(self, length, width, **kwargs):
        self.length = length
        self.width = width
        super().__init__(**kwargs)

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * self.length + 2 * self.width

class Square(Rectangle):
    def __init__(self, length, **kwargs):
        super().__init__(length=length, width=length, **kwargs)

class Triangle:
    def __init__(self, base, height, **kwargs):
        self.base = base
        self.height = height
        super().__init__(**kwargs)

    def tri_area(self):
        return 0.5 * self.base * self.height

class RightPyramid(Square, Triangle):
    def __init__(self, base, slant_height, **kwargs):
        self.base = base
        self.slant_height = slant_height
        kwargs["height"] = slant_height
        kwargs["length"] = base
        super().__init__(base=base, **kwargs)

    def area(self):
        base_area = super().area()
        perimeter = super().perimeter()
        return 0.5 * perimeter * self.slant_height + base_area

    def area_2(self):
        base_area = super().area()
        triangle_area = super().tri_area()
        return triangle_area * 4 + base_area

Multiple inheritance can get tricky quickly. A simple use case that is common in the field is to write a mixin. A mixin is a class that doesn’t care about its position in the hierarchy, but just provides one or more convenience methods:

class SurfaceAreaMixin:
    def surface_area(self):
        surface_area = 0
        for surface in self.surfaces:
            surface_area += surface.area(self)

        return surface_area

class Cube(Square, SurfaceAreaMixin):
    def __init__(self, length):
        super().__init__(length)
        self.surfaces = [Square, Square, Square, Square, Square, Square]

class RightPyramid(Square, Triangle, SurfaceAreaMixin):
    def __init__(self, base, slant_height):
        self.base = base
        self.slant_height = slant_height
        self.height = slant_height
        self.length = base
        self.width = base

        self.surfaces = [Square, Triangle, Triangle, Triangle, Triangle]

Here’s what you get:

>>>

>>> cube = Cube(3)
>>> cube.surface_area()
54

What is the problem with multiple inheritance in Python?

The Problem with Multiple Inheritance If you allow multiple inheritance then you have to face the fact that you might inherit the same class more than once. In Python as all classes inherit from object, potentially multiple copies of object are inherited whenever multiple inheritance is used.

Can a Python class inherit from multiple classes?

A class can be derived from more than one base class in Python, similar to C++. This is called multiple inheritance. In multiple inheritance, the features of all the base classes are inherited into the derived class. The syntax for multiple inheritance is similar to single inheritance.

Can we use super in multiple inheritance in Python?

Supercharge Your Classes With Python super() Python supports inheritance from multiple classes.

How do I inherit two parent classes?

Inheritance is the mechanism to achieve the re-usability of code as one class(child class) can derive the properties of another class(parent class). It also provides transitivity ie. if class C inherits from P then all the sub-classes of C would also inherit from P.