13: Multiple Inheritance¶
Summary¶
Single inheritance doesn’t satisfy all of the requirements of domain modeling and doesn’t allow for modifications to inheritance chains to introduce useful mixin
Multiple inheritance is superficially easy, but it changes the hierarchy of classes into a more general graph where each class can be inherited more than once
If you recast the idea of multiple inheritance asa way of modifying inheritance chains then the solution to the diamond problem is to linearize the inheritance graph to produce an inheritance chain in the form of the mro
The C3 algorithm can produce a linearized inheritance chain that has good properties
Not all cases of multiple inheritance can be linearized using C3 but these are problematic and best avoided
Once you understand how the mri is constructed you can start to control the way that multiple inheritance determines the mro and you can make informed choices of how to define the inheritance
The resulting mro allows each class to have a unique superclass and the super() function makes use of this to return a single superclass for any class in the mro
As the inheritance chain can vary, a given class may have a different superclass depending on which mro you are using
To make cooperative multiple inheritance work you have to make sure that each class calls the __init__ of its superclass and makes use of the dwindling parameter pattern to allow for the superclass to vary
Program¶
"""
Multiple Inheritance and MRO Demonstration
This program shows:
- Why single inheritance is sometimes not enough
- How multiple inheritance creates a graph (not a simple chain)
- How Python resolves this using MRO (Method Resolution Order)
- How super() works with the MRO
- How to properly design cooperative multiple inheritance using
the "dwindling parameter pattern"
"""
# -------------------------------
# BASE CLASS
# -------------------------------
class Base:
def __init__(self, **kwargs):
"""
Base class initializer.
Uses **kwargs to support cooperative multiple inheritance.
"""
print("Base initialized")
super().__init__(**kwargs)
# -------------------------------
# MIXIN CLASSES
# -------------------------------
class LoggerMixin:
def __init__(self, log_enabled=True, **kwargs):
"""
A mixin that adds logging capability.
Demonstrates modifying the inheritance chain.
"""
self.log_enabled = log_enabled
print("LoggerMixin initialized")
super().__init__(**kwargs)
class DatabaseMixin:
def __init__(self, db_connected=True, **kwargs):
"""
Another mixin that adds database functionality.
"""
self.db_connected = db_connected
print("DatabaseMixin initialized")
super().__init__(**kwargs)
# -------------------------------
# MAIN CLASS USING MULTIPLE INHERITANCE
# -------------------------------
class Application(LoggerMixin, DatabaseMixin, Base):
def __init__(self, app_name, **kwargs):
"""
Final class that combines multiple behaviors.
Demonstrates:
- Multiple inheritance
- MRO linearization (C3 algorithm)
- Cooperative initialization using super()
"""
self.app_name = app_name
print("Application initialized")
super().__init__(**kwargs)
# -------------------------------
# RUN EXAMPLE
# -------------------------------
if __name__ == "__main__":
print("MRO of Application:")
for cls in Application.__mro__:
print(" ", cls.__name__)
print("\nCreating Application instance:\n")
app = Application(
app_name="MyApp",
log_enabled=True,
db_connected=False
)
Program Output¶
MRO of Application:
Application
LoggerMixin
DatabaseMixin
Base
object
Creating Application instance:
Application initialized
LoggerMixin initialized
DatabaseMixin initialized
Base initialized