Python Multiple Inheritance and MRO: How It Actually Works
Think about how you inherit traits from your parents. You might have your mom's eyes and your dad's sense of humor. You didn't pick one parent — you got traits from both.
Python classes can do the same thing. A class can inherit from two or more parent classes at once, combining their abilities. This is called multiple inheritance, and Python is one of the few popular languages that supports it directly.
Multiple inheritance is powerful, but it introduces a tricky question: if two parents define the same method, which one wins? In this tutorial, you'll learn how Python answers that question using the Method Resolution Order (MRO), how to avoid the famous diamond problem, and how to use mixins to keep things clean.
How Does Basic Multiple Inheritance Work?
To inherit from multiple classes, list them all inside the parentheses separated by commas. The child class gets every method and attribute from all of its parents.
The Duck class didn't define fly() or swim(), but it can use both because it inherits from Flyer and Swimmer. Each parent contributes its own methods, and the child gets them all.
What Is the Diamond Problem?
The diamond problem happens when a class inherits from two parents that share the same grandparent. If you draw the inheritance tree, it looks like a diamond shape.
Here's the situation: class A defines a method. Classes B and C both inherit from A. Then class D inherits from both B and C. When D calls that method, which version does it get — B's or C's?
Python picks B's version because B is listed first in the class definition: class D(B, C). But the full story is more interesting than just "first wins." Python uses an algorithm called C3 linearization to decide the exact order.
How Does Python Decide Which Method to Call? (MRO)
The Method Resolution Order (MRO) is the exact sequence Python follows when looking up a method. It's like a search order: Python checks class D first, then B, then C, then A, then object. The first match wins.
You can see any class's MRO by calling the .__mro__ attribute or the mro() method.
The order is D -> B -> C -> A -> object. Python uses the C3 linearization algorithm to compute this. The key rules are: children come before parents, and the left-to-right order from the class definition is preserved.
How Does super() Work with Multiple Inheritance?
Here's where it gets really interesting. super() doesn't just call the parent class — it calls the next class in the MRO. This means super() inside B might call C instead of A, depending on the actual object's MRO.
Notice the order: D calls B (next in MRO), B calls C (not A!), C calls A, and A has no super call to make. Each class in the MRO gets called exactly once. This is called cooperative multiple inheritance.
The **kwargs pattern is the standard way to make __init__ cooperative. Each class takes what it needs and passes the rest up the chain. The Base class at the end absorbs any leftover keyword arguments.
What Are Mixins and Why Should You Use Them?
A mixin is a small class designed to add a specific feature to other classes. It's not meant to stand on its own — it's a building block you snap onto existing classes. Think of it like a phone case with a built-in wallet. The case isn't a phone, but it adds wallet functionality to any phone.
By convention, mixin class names end with Mixin. This makes their purpose immediately clear to anyone reading the code.
Mixins are the most practical use of multiple inheritance. Instead of building deep, complex hierarchies, you compose classes from small, reusable pieces. This approach is easier to test, understand, and maintain.
A common real-world pattern is combining a main base class with utility mixins. For example, a web framework might let you write class MyView(LoginRequiredMixin, ListView) — one mixin for authentication and one base class for the page type.
When listing parents, put mixins first and the main base class last. This ensures mixin methods take priority over base class defaults, and the MRO stays predictable.
Practice Exercises
Create three classes:
Runner with a method run() that returns 'Running fast'Climber with a method climb() that returns 'Climbing high'Adventurer that inherits from both Runner and Climber and adds a method describe() that returns 'I can run and climb'Create an Adventurer instance and print all three methods.
What does this code print? Think about the Method Resolution Order.
class A:
def who(self):
return 'A'
class B(A):
pass
class C(A):
def who(self):
return 'C'
class D(B, C):
pass
d = D()
print(d.who())Remember: Python follows the MRO, not just the direct parent.
This code uses multiple inheritance but the __init__ methods don't cooperate properly. Fix it so the output is:
Alice, age 25Hint: use **kwargs to pass remaining arguments up the MRO chain.
Create a TimestampMixin class with a method created_info() that returns the string 'Created by <class_name>' where <class_name> is the actual class name. Then create a Document class that inherits from TimestampMixin and has an __init__ that takes a title. Print the title and the created_info().
Expected output:
My Report
Created by DocumentWhat does this code print? Trace through the super() calls following the MRO.
class Base:
def show(self):
print('Base')
class Left(Base):
def show(self):
print('Left')
super().show()
class Right(Base):
def show(self):
print('Right')
super().show()
class Child(Left, Right):
def show(self):
print('Child')
super().show()
Child().show()