Python Inheritance Common Practices and Pitfalls: Diamond Problem, Mixins, and Others
Last Updated on November 9, 2023 by Editorial Team
Author(s): Muttineni Sai Rohith
Originally published on Towards AI.
Inheritance, like any other concept in OOPs, allows developers to reuse the code and develop elegant and scalable software solutions. Keeping in mind the vast community of Python developed multiple modules and logics. However, as with any concept, inheritance has some common pitfalls β like the Diamond Problem, Interface inheritance challenges, and usage of Mixins.
Inheritance is a fundamental concept in Object Oriented Programming, where a new class inherits its parentβs class and reuses all the methods and variables defined in the parent class. Therefore, it promotes Code Reusability, Scalability and Modularity.
Multiple Inheritance
Multiple Inheritance allows a class to inherit attributes and methods from more than one base class. These means a derived class can inherit multiple classes simultaneously.
class fly:
can_fly = True
class swim:
can_swim = True
class Duck(swim, fly):
pass
d = Duck()
print(d.can_fly) #True
print(d.can_swim) #True
Above is a simple example of multiple inheritance and how it can be helpful, If we want to write a python program about birds and mention some specialities independently in classes and want to reuse them when discussing about a particular bird, In Instance of Duck β which can swim, fly and also walk. In these cases Multiple Inheritance is helpful.
There are programming languages which does not support multiple inheritance, although it provides us flexibility to reuse the code, It can introduce new challenges such as Diamond Problem where a confusion can arise when a class inherits from multiple classes which has a common base.
Diamond Problem
Diamond Problem particularly arises when a child class inherits from multiple parent classes which has a common base. This creates ambiguity in which order the overriden methods should execute. Letβs see the below example β
class Animal:
def speak(self):
print("Animal speaks")
class Bird(Animal):
def speak(self):
print("Bird chirps")
class Fish(Animal):
def speak(self):
print("Fish bubbles")
class Amphibian(Bird, Fish):
pass
Here Amphibian class, inherits both Fish and Bird classes which has a common base Animal, As speak method is getting overriden in both parent classes, it creates confusion for Amphibian class to invoke a method when speak method is called. Due to this reason few programming languages doesnot support multiple inheritance.
But python with the help of C3 Linearization, addressess Diamond problem using Method Resolution Order(MRO) and enables inheritance.
Method Resolution Order(MRO)
In Python, C3 linearization introduced in 2.3 Version governs Method Resolution Order. It determines the order in which classes has to be searched to find an appropriate method or variable. C3 Linearization algorithms takes the order of inheritance into account and derives the relationship between the classes.
When a method is called, Python starts by searching it in the current class and if not found, Python follows the MRO to search in the base classes. And MRO will be left to right in case of multiple inheritance and bottom to top in case of Multilevel Inhertiance. This allows developers to use multiple inheritance effecitvely without any ambiguities.
Now if we check the output, speak in Bird class is referred first as it is called first.
Amphibian().speak()
# Output - Bird chirps
while MRO makes sure we donβt get errors, Mixins can also be used to avoid multiple inheritance and provide additional functionalities.
Mixins
Mixins are classes designed to be small and focus on single functionality. They provide a way to share functionality among multiple classes without using traditional inheritance. They help in preventing the Diamond problem and avoids deep hierarchies.
class JSONMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class JSONPerson(Person, JSONMixin):
pass
person = JSONPerson("Alice", 30)
print(person.to_json())
Other Inheritance Pitfalls
- Inherited classes are tightly coupled with base classes, and whenever base class functionality is changed, One has to test all the inherited classes to ensure error-free code. For this reason, structuring the base class and careful inheritance is necessary. Maintain wrapper methods in base class and as much as possible donβt inherit or change maintain static code in child class. Donβt select a fragile and unmaintainable base class.
- Incorrect method overriding or misuse of super() methods can produce error-prone code. Donβt maintain complex hierarchies as python doesnot force encapsulation and method signatures, there can be issues in runtime if Proper overriding is not done.
- Inheritance vs. Composition: Inheritance is not the best solution in all the cases, Compositions in general creates a complex object by combining simple objects rather than inheriting the behaviour from child classes. Chose Composition over inheritance whenever possible as it also promotes code reusability and modularity and avoids pitfalls in inheritance such as Diamond problem, Tightly Coupling and inflexibilities.
So thatβs all my take about common inheritance pitfalls. I hope this helpsβ¦Happy Codingβ¦
References:
https://www.python.org/download/releases/2.3/mro/
Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming aΒ sponsor.
Published via Towards AI