πΉ 1. Diamond Problem β Ambiguity in Method Resolution Order (MRO)
Copy
class A:
def greet(self):
print("Hello from A")
class B(A):
pass
class C(A):
def greet(self):
print("Hello from C")
class D(B, C):
pass
d = D()
d.greet() # Hello from C
print(D.mro()) # [D, B, C, A, object]
π Issue:
D inherits from both B and C, but C overrides greet.
Method Resolution Order (MRO) determines that C's method is called.
πΉ 2. MRO Conflicts β Inconsistent Hierarchy
Copy
π Issue:
The MRO becomes inconsistent when D and E have different base class orders.
πΉ 3. Super() Calls in Multiple Inheritance
Copy
π Issue:
Calls super() correctly based on MRO:
Copy
This works if all classes use super() correctly.
πΉ 4. Using super() Incorrectly
Copy
π Issue:
Copy
A.__init__() gets called twice!
Fix this by using super()only if all classes cooperate.
πΉ 5. Attribute Name Conflicts
Copy
π Issue:
Both A and B define value.
MRO picks Aβs version, potentially leading to bugs.
πΉ 6. Overriding Methods in Unexpected Ways
Copy
π Issue:
The method in C is ignored because B comes first in D(B, C).
πΉ 7. Calling Parent Methods Explicitly
Copy
π Issue:
Copy
Bad practice: Calls A.show() twice.
Better to use super() to avoid redundant calls.
πΉ 8. Using isinstance with Multiple Base Classes
Copy
π Issue:
isinstance(d, A) is True, even though D never explicitly inherits from A.
πΉ 9. Multiple Base Classes with super()
Copy
π Correct MRO prevents double initialization:
Copy
Ensure all classes use super() correctly to prevent duplicate calls.
πΉ 10. Avoiding Multiple Inheritance with Composition
Copy
π Why this works better?
Composition over inheritance avoids MRO issues.
More maintainable & modular than multiple inheritance.
π Summary
Pitfall
Issue
Solution
Diamond Problem
MRO ambiguity
Use super() properly
MRO Conflicts
Inconsistent ordering
Follow a clear class hierarchy
Super Calls
Wrong method calls
Ensure all classes use super()
Attribute Conflicts
Overwritten attributes
Use unique attribute names
Method Overriding
Unexpected behavior
Check mro() before overriding
Explicit Parent Calls
Repeating method calls
Use super() instead
isinstance Issues
Inheriting unintended behavior
Avoid unnecessary multiple inheritance
Multiple __init__ Calls
Calls parent multiple times
Use super() correctly
Super in Multiple Base Classes
Wrong super() order
Use cooperative multiple inheritance
Alternative: Composition
Complex MRO issues
Prefer composition over inheritance
π Best Practices
β Prefer single inheritance unless multiple inheritance is absolutely needed. β Use MRO (print(Class.mro())) to check method resolution order. β Always use super() to avoid redundant method calls. β Consider composition as an alternative.
class A: pass
class B(A): pass
class C: pass
class D(B, C): pass # Works
class E(C, B): pass # Works
# class F(D, E): pass # β TypeError: Cannot create consistent method resolution order (MRO)
class A:
def greet(self):
print("A")
class B(A):
def greet(self):
super().greet()
print("B")
class C(A):
def greet(self):
super().greet()
print("C")
class D(B, C):
def greet(self):
super().greet()
print("D")
d = D()
d.greet()
A
C
B
D
class A:
def __init__(self):
print("A initialized")
class B(A):
def __init__(self):
print("B initialized")
super().__init__() # Calls A
class C(A):
def __init__(self):
print("C initialized")
super().__init__() # Calls A again!
class D(B, C):
def __init__(self):
print("D initialized")
super().__init__()
d = D()
D initialized
B initialized
C initialized
A initialized
class A:
def __init__(self):
self.value = 10
class B:
def __init__(self):
self.value = 20
class C(A, B):
pass
c = C()
print(c.value) # 10 (from A)
class A:
def work(self):
print("A working")
class B(A):
def work(self):
print("B working")
class C(A):
def work(self):
print("C working")
class D(B, C):
pass
d = D()
d.work() # B working (due to MRO)
class A:
def show(self):
print("A")
class B(A):
def show(self):
A.show(self)
print("B")
class C(A):
def show(self):
A.show(self)
print("C")
class D(B, C):
def show(self):
B.show(self)
C.show(self)
print("D")
d = D()
d.show()
A
B
A
C
D
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
d = D()
print(isinstance(d, B)) # True
print(isinstance(d, C)) # True
print(isinstance(d, A)) # True
class A:
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
super().__init__()
class C(A):
def __init__(self):
print("C init")
super().__init__()
class D(B, C):
def __init__(self):
print("D init")
super().__init__()
d = D()
D init
B init
C init
A init
class Engine:
def start(self):
print("Engine starting...")
class Wheels:
def rotate(self):
print("Wheels rotating...")
class Car:
def __init__(self):
self.engine = Engine()
self.wheels = Wheels()
def drive(self):
self.engine.start()
self.wheels.rotate()
print("Car moving...")
car = Car()
car.drive()