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. Because every class's super() call resolves against
this single shared MRO (not against its own direct parent), 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 (e.g., one
class says B before C, another says C before B, and something
inherits from both), C3 has no valid linearization and Python raises
TypeError: Cannot create a consistent method resolution order at class
definition time, rather than silently picking an arbitrary order.
Interview-ready summary: 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.