Thursday, May 12, 2022

Multiple Inheritance in Python

When a sub class is derived from more than one super class it is known as multiple inheritance. In this post we'll see support for multiple inheritance in Python, what all conflicts may occur because of multiple inheritance and what is method resolution order in Python and how it helps in multiple inheritance scenarios.


Python multiple inheritance

Python, unlike Java, supports multiple inheritance. Python has a well designed approach for tackling diamond problem in multiple inheritance using method resolution order.

Here is a figure depicting multiple inheritance where class C inherits from both class A and class B.

Multiple Inheritance

Python syntax for multiple inheritance

A child class DerivedClass inheriting from super classes Parent1, Parent2, Parent3 and so on.

class DerivedClass(Parent1, Parent2, Parent3 ....):

Python multiple inheritance example

class Parent1:
  def display(self):
    print('In display method of Parent1')

class Parent2:
  def display_value(self):
    print('In display method of Parent2')

#inheriting both Parent1 and Parent2
class Child(Parent1, Parent2):
  def display_child(self):
    print('In display method of Child')

obj = Child()
obj.display()
obj.display_value()
obj.display_child()

Output

In display method of Parent1
In display method of Parent2
In display method of Child

Since members of all the parent classes are inherited by the child class so you can access methods in classes Parent1 and Parent2 using an object of class Child.

Conflicts due to multiple inheritance

Example shown above is a simple example of multiple inheritance in Python but in practical world things won’t be that simple. Now is the time to go into diamond problem which can crop up due to multiple inheritance.

Consider the following scenario where you have two super classes and a sub class inheriting both these classes. Each of these classes have their own __init__() method and child class calls the parent class constructor using super. Now the question is which parent class’ constructor would be called.

class Parent1:
  def __init__(self):
    print('In init method of Parent1')
  def display(self):
   print('In display method of Parent1')

class Parent2:
  def __init__(self):
    print('In init method of Parent2')
  def display_value(self):
    print('In display method of Parent2')

#inheriting both Parent1 and Parent2
class Child(Parent1, Parent2):
  def __init__(self):
    super().__init__()
    print('In init method of Child')
  def display_child(self):
    print('In display method of Child')

obj = Child()

Output

In init method of Parent1
In init method of Child

As you can see __init__() method of only Parent1 is called.

Same way if super classes have a method having the same name then which method has to be called?

class Parent1:
  def display(self):
    print('In display method of Parent1')

class Parent2:
  def display(self):
    print('In display method of Parent2')

#inheriting both Parent1 and Parent2
class Child(Parent1, Parent2):
  def display_child(self):
    print('In display method of Child')

obj = Child()
obj.display()
obj.display_child()

Output

In display method of Parent1
In display method of Child

In the example both super classes Parent1 and Parent2 have a method display(), when display() method is called using child class object display() method of Parent1 is called.

If you are wondering how is Python deciding which constructor or which method is to be called in such conflict scenarios you need to know about method resolution order in Python.

Method resolution order in Python

When you have multiple inheritance in Python any member of the class (field or method) is first searched in the child class. If not found there then the search moves to parent classes in depth-first, left to right order without searching the same class twice. This ordering is known as Method Resolution Order (MRO) in Python which works on the following three principles.

  1. Search in the child class first before moving moving higher up to the parent classes.
  2. Search in the parent classes is done in left to right order, for example if class C is inheriting from classes P1 and P2 - class C(P1, P2) then class P1 is searched first then class P2.
  3. No class is visited more than once.

MRO of any class can be viewed using Classname.__mro__ class attribute which returns a tuple or using Classname.mro() method which returns a list.

To understand MRO fully you should also remember that in Python all the classes are derived from the class object. When you write a class definition as class A it is equivalent to class A(object) implicitly it is inheriting from class object.

Calling constructors of all the super classes in multiple inheritance

Now when you understand method resolution order let’s revisit the scenario where constructor of the super class is called from the child class using super(). Now the code is changed keeping MRO in view so that constructors of both Parent1 and Parent2 are called.

class Parent1:
  def __init__(self, x, **kwargs):
    print('In init method of Parent1')
    self.x = x
    super().__init__(**kwargs)
  def display(self):
    print('In display method of Parent1', self.x)

class Parent2:
  def __init__(self, y, **kwargs):
    print('In init method of Parent2')
    self.y = y
    super().__init__(**kwargs)
  def display_value(self):
    print('In display method of Parent2', self.y)

#inheriting both Parent1 and Parent2
class Child(Parent1, Parent2):
  def __init__(self, z, **kwargs):
    print('In init method of Child')
    super().__init__(**kwargs)
    self.z = z

  def display_child(self):
    print('In display method of Child', self.z)

obj = Child(x=10, y='Hello', z=20)

obj.display()
obj.display_value()
obj.display_child()
print(Child.mro())

Output

In init method of Child
In init method of Parent1
In init method of Parent2
In display method of Parent1 10
In display method of Parent2 Hello
In display method of Child 20
[<class '__main__.Child'>, <class '__main__.Parent1'>, <class '__main__.Parent2'>, <class 'object'>]

Following image will help you understand the class hierarchy in the example and to understand how method resolution happens.

method resolution order in python

When object of class Child is created its constructor is called where super().__init__() is encountered which calls the constructor of the super class at the left side (Parent1). With in the constructor of Parent1 again super().__init__() is called, since super class of Parent1 is object so it will try to execute the constructor of object class. But there is no constructor in object class so search moves to right side i.e. Parent2 class and its constructor is executed. There is super().__init__() call in the constructor of Parent2 class too which means again the constructor of object class. Since object class is already visited so execution stops here.

In the example **kwargs (keyword variable arguments) are used to pass the parameters. Using keyword parameters you can identify the parameters by their names.

Same order of execution is also displayed by executing the mro() method for Child class.

Method resolution order with methods in multiple inheritance

If you call parent class method using super() from a child class inheriting from multiple classes then again order of execution is determined by MRO.

class Parent1:
  def display(self):
    print('In display method of Parent1')
    super().display()

class Parent2:
  def display(self):
    print('In display method of Parent2')
    super().display()

#inheriting both Parent1 and Parent2
class Child(Parent1, Parent2):
  def display(self):
    print('In display method of Child')
    super().display()

obj = Child()
obj.display()

Output

In display method of Child
In display method of Parent1
In display method of Parent2

Again by applying method resolution order you can determine the order in which method in each class is executed. Initially display() method of Child class is executed then display() method of Parent1 class is executed as it is on the left side. From Parent1 super().display() call searches display() method in object class (super class of Parent1) which is not found so search continues on the right side class which is Parent2.

That's all for this topic Multiple Inheritance in Python. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. Python Installation on Windows
  2. Method Overriding in Python
  3. Interface in Python
  4. Check String Empty or Not in Python
  5. Python break Statement With Examples

You may also like-

  1. Passing Object of The Class as Parameter in Python
  2. Abstract Class in Python
  3. Python Exception Handling - try,except,finally
  4. Python Program to Check Prime Number
  5. Why no Multiple Inheritance in Java
  6. Race Condition in Java Multi-Threading
  7. Spring Web Reactive Framework - Spring WebFlux Tutorial
  8. Shuffle And Sort Phases in Hadoop MapReduce