How to use multiple inheritance in Python

Subscribe to my newsletter and never miss my upcoming articles

One of the cool things that Python provides is multiple inheritance. This feature allows us to make a class that inherits characteristics and behaviors from more than one parent class, which can be handy sometimes. But, to be honest, it can be tricky as well. In some other languages, like Java or C#, we can't make it through classes, but we can mimic this implementation with interfaces.

How does it work?

To be more descriptive I will create 3 classes. The first one will be the Hello class which will only have the introduce_me method. The second one will be Robot (inherits Hello) which will have code and origin instance variables and get_energy_level method. The final one will be Human (inherits Hello) that contains first_name and last_name variables and start_conversation method. Code:

import random


class Hello:
    def introduce_me(self):
        return f'I am {self.__class__.__name__}.'


class Robot(Hello):
    def __init__(self, code, origin):
        super(Robot, self).__init__()
        self.code = code
        self.origin = origin

    def get_energy_level(self):
        return f'The energy level of #{self.code} is {random.randint(1, 100)}%'


class Human(Hello):
    def __init__(self, first_name, last_name):
        super(Human, self).__init__()
        self.first_name = first_name
        self.last_name = last_name

    def start_conversation(self):
        return f'Hi, I am {self.first_name} {self.last_name}. How are you?'

To test this implementation I created a main module with the following code:

from models import Human, Robot

if __name__ == '__main__':
    print('HUMAN'.center(100, '='))
    human = Human('Kosta', 'Kupresak')
    print(human.start_conversation())
    print(human.introduce_me())

    print('ROBOT'.center(100, '='))
    robot = Robot('F3R', 'SRB')
    print(robot.get_energy_level())
    print(robot.introduce_me())

As you can see this is an example of single inheritance. The result of the main module is:

how-to-use-multiple-inheritance-in-python-1.PNG

Now, let's create the Cyborg model and update the main file:

class Cyborg(Human, Robot):
    def __init__(self, first_name, last_name, code, origin, superpower):
        super(Cyborg, self).__init__(first_name=first_name, last_name=last_name, code=code, origin=origin)
        self.superpower = superpower
print('CYBORG'.center(100, '='))
cyborg = Cyborg('Ana', 'Anic', '3FA', 'SRB', 'Flying')
print(f'My code is {cyborg.code} and I am from {cyborg.origin}.')
print(cyborg.start_conversation())
print(f'My superpower is {cyborg.superpower}.')
print(cyborg.introduce_me())

Because of the super(Cyborg, self).__init__(first_name=first_name, last_name=last_name, code=code, origin=origin) line in the constructor we need to alter all constructors from the classes which Cyborg inherits or the multiple inheritance won't work. The easy fix is adding the **kwargs parameter to init methods and passing it like in the following example:

import random


class Hello:
    def __init__(self, **kwargs):
        pass

    def introduce_me(self):
        return f'I am {self.__class__.__name__}.'


class Robot(Hello):
    def __init__(self, code, origin, **kwargs):
        super(Robot, self).__init__(**kwargs)
        self.code = code
        self.origin = origin

    def get_energy_level(self):
        return f'The energy level of #{self.code} is {random.randint(1, 100)}%'


class Human(Hello):
    def __init__(self, first_name, last_name, **kwargs):
        super(Human, self).__init__(**kwargs)
        self.first_name = first_name
        self.last_name = last_name

    def start_conversation(self):
        return f'Hi, I am {self.first_name} {self.last_name}. How are you?'


class Cyborg(Human, Robot):
    def __init__(self, first_name, last_name, code, origin, superpower):
        super(Cyborg, self).__init__(first_name=first_name, last_name=last_name, code=code, origin=origin)
        self.superpower = superpower

Now the Cyborg class will first call Human constructor and all unused parameters will be passed to the next class in the inheritance line (Robot). That is the purpose of the **kwargs a.k.a. keyword arguments. To conclude, all information will be successfully shared between all classes and if we run the main module we will get the following output:

how-to-use-multiple-inheritance-in-python-2.PNG

Method Resolution Order

If we have methods with the same name in base classes, then Python will look from the left to the right side (Method Resolution Order). Imagine that we add a method what_i_am_more to the Human and Robot classes:

class Robot(Hello):
    def __init__(self, code, origin, **kwargs):
        super(Robot, self).__init__(**kwargs)
        self.code = code
        self.origin = origin

    def get_energy_level(self):
        return f'The energy level of #{self.code} is {random.randint(1, 100)}%'

    def what_i_am_more(self):
        return 'Robot!'


class Human(Hello):
    def __init__(self, first_name, last_name, **kwargs):
        super(Human, self).__init__(**kwargs)
        self.first_name = first_name
        self.last_name = last_name

    def start_conversation(self):
        return f'Hi, I am {self.first_name} {self.last_name}. How are you?'

    def what_i_am_more(self):
        return 'Human!'

Now, if we execute the following code...

print('METHOD RESOLUTION ORDER'.center(100, '='))
humanoid_cyborg = Cyborg('Pera', 'Peric', '4MX', 'SRB', 'Replication')
print(humanoid_cyborg.what_i_am_more())

...we will get:

how-to-use-multiple-inheritance-in-python-3.PNG

The reason is that the Human class is the first in the inheritance line class Cyborg(Human, Robot). If we switch the order to class Cyborg(Robot, Human) we will get a different output:

how-to-use-multiple-inheritance-in-python-4.PNG

Basically, just follow the order of classes in the class definition.

I hope you liked today's article. All examples are available at github.com/kostakuu/how-to-use-multiple-inh..

See you soon!

No Comments Yet