How to use multiple inheritance in Python
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:
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:
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:
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:
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