Multiple Inheritance
Inherit from multiple parent classes at once — how Python’s Method Resolution Order (MRO) decides which method wins.
"Multiple inheritance is powerful when used for small, focused mixins. The MRO is Python's promise: there will always be one clear, predictable order in which classes are searched."
— ShurAIWhat is Multiple Inheritance?
A class can inherit from more than one parent at once. It gets all the attributes and methods from each parent:
class Flyable:
def fly(self):
print(f"{self.__class__.__name__} is flying")
class Swimmable:
def swim(self):
print(f"{self.__class__.__name__} is swimming")
class Duck(Flyable, Swimmable): # inherits from both
def quack(self):
print("Quack!")
d = Duck()
d.fly() # Duck is flying — from Flyable
d.swim() # Duck is swimming — from Swimmable
d.quack() # Quack! — Duck's own
The MRO — Method Resolution Order
When the same method exists in multiple parents, Python uses the MRO to decide which one wins. The MRO is the exact order Python searches classes for a method. Use .mro() or __mro__ to see it:
print(Duck.mro())
# [Duck, Flyable, Swimmable, object]
# Python searches left-to-right: Duck → Flyable → Swimmable → object
# First class in the MRO that has the method wins
Conflict Resolution — Same Method in Both Parents
class A:
def greet(self): print("Hello from A")
class B:
def greet(self): print("Hello from B")
class C(A, B): # A is listed first → A wins
pass
class D(B, A): # B is listed first → B wins
pass
C().greet() # Hello from A
D().greet() # Hello from B
print(C.mro()) # [C, A, B, object]
Mixins — the Right Use of Multiple Inheritance
The most Pythonic pattern is mixins: small, focused classes that add one specific behaviour. They are designed to be combined:
class JsonMixin:
"""Mixin: add .to_json() to any class."""
def to_json(self):
import json
return json.dumps(self.__dict__)
class LogMixin:
"""Mixin: add .log() to any class."""
def log(self):
print(f"[LOG] {self.__class__.__name__}: {self.__dict__}")
class User(JsonMixin, LogMixin):
def __init__(self, name, email):
self.name = name
self.email = email
u = User("Riya", "riya@shurai.com")
u.log() # [LOG] User: {'name': 'Riya', 'email': 'riya@shurai.com'}
print(u.to_json()) # {"name": "Riya", "email": "riya@shurai.com"}
super() Across the MRO Chain
class Base:
def __init__(self): print("Base init")
class A(Base):
def __init__(self):
print("A init")
super().__init__() # calls next in MRO, not always Base!
class B(Base):
def __init__(self):
print("B init")
super().__init__()
class C(A, B):
def __init__(self):
print("C init")
super().__init__()
C()
# C init → A init → B init → Base init
# MRO: [C, A, B, Base, object] — each super() goes to the next in line
"When in doubt, keep it simple: inherit from one main class and add mixins for extra capabilities. Deep diamond hierarchies are where bugs love to hide."
— ShurAI🧠 Quiz — Q1
What does Python's MRO determine?
🧠 Quiz — Q2
Class C(A, B) and both A and B have a method greet(). Which version does C().greet() call?
🧠 Quiz — Q3
What is a mixin?
🧠 Quiz — Q4
How do you see the MRO of a class called Duck?