What is the diamond problem in multiple inheritance, and how does Python resolve it?
Quick Answer
The diamond problem occurs when a class inherits from two classes that share a common ancestor (D(B, C) where both B and C inherit from A). Naive resolution could visit A twice or pick an ambiguous method. Python resolves it with C3 linearization, which guarantees each ancestor appears exactly once in the MRO, in a consistent order, so super() calls correctly chain through B and C before reaching A a single time.
Detailed Answer
The shape of the problem
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
super().method()
class C(A):
def method(self):
print("C.method")
super().method()
class D(B, C):
def method(self):
print("D.method")
super().method()
D().method()
# D.method
# B.method
# C.method
# A.method
Both B and C inherit from A, so D, which inherits from both,
forms a "diamond" (D → B → A, D → C → A). A naive depth-first search
would call A.method after B (since B's parent is A), then call
A.method again after C — visiting A twice and giving C no
chance to run before A does.
How C3 linearization fixes it
D.__mro__ is [D, B, C, A, object] — A appears exactly once, after
both B and C. Every class's super() call resolves against this
single shared MRO, not against its own direct parent. So the chain
D → B → C → A runs each class's method exactly once, in a well-defined
order that respects D's declared base order (B before C).
Why this matters practically: cooperative mixins
This is exactly the mechanism that makes mixin composition safe:
class Base:
def __init__(self):
print("Base init")
class Mixin1(Base):
def __init__(self):
print("Mixin1 init")
super().__init__()
class Mixin2(Base):
def __init__(self):
print("Mixin2 init")
super().__init__()
class Combined(Mixin1, Mixin2):
pass
Combined()
# Mixin1 init
# Mixin2 init
# Base init
Base.__init__ runs exactly once, after both mixins, not once per mixin
and not skipped. Without C3's guarantee of a single consistent
linearization, composing mixins that share a common base would be far
more error-prone.
When Python can't resolve it
If the declared base-class orders are mutually contradictory (one
class says B before C, another says C before B, and something
inherits from both), C3 has no valid linearization. Python raises
TypeError: Cannot create a consistent method resolution order at class
definition time, rather than silently picking an arbitrary order.
The diamond problem is the ambiguity of
which shared ancestor's method runs, and in what order, when two parent
classes share a common base. Python's C3 linearization algorithm computes
a single MRO where every ancestor appears exactly once in a consistent
order, which is what makes super()-based cooperative multiple
inheritance well-defined instead of ambiguous.