OOP Advanced
Multiple Inheritance
Many Parents
Your game character needs to be Movable, Damageable, and Renderable. Python lets a class inherit from multiple parents. The Method Resolution Order (MRO) determines which parent's method is used when names collide.
Basic multiple inheritance
Inherit from two parent classes.
# Basic Multiple Inheritance
class Swimmer:
"""Can swim."""
def swim(self):
return f"{self.__class__.__name__} is swimming"
def describe(self):
return "I can swim"
class Flyer:
"""Can fly."""
def fly(self):
return f"{self.__class__.__name__} is flying"
def describe(self):
return "I can fly"
class Walker:
"""Can walk."""
def walk(self):
return f"{self.__class__.__name__} is walking"
def describe(self):
return "I can walk"
# Single inheritance - one parent
class Fish(Swimmer):
pass
# Multiple inheritance - two parents
class Duck(Swimmer, Flyer, Walker):
"""Ducks can swim, fly, and walk!"""
pass
# Another multiple inheritance example
class Penguin(Swimmer, Walker):
"""Penguins can swim and walk, but not fly."""
pass
def main():
print("=== Basic Multiple Inheritance ===\n")
# Single inheritance
print("--- Single Inheritance (Fish) ---")
fish = Fish()
print(fish.swim())
print(f"describe(): {fish.describe()}")
# Multiple inheritance - Duck
print("\n--- Multiple Inheritance (Duck) ---")
duck = Duck()
print(duck.swim())
print(duck.fly())
print(duck.walk())
print(f"describe(): {duck.describe()}")
# Multiple inheritance - Penguin
print("\n--- Multiple Inheritance (Penguin) ---")
penguin = Penguin()
print(penguin.swim())
print(penguin.walk())
print(f"describe(): {penguin.describe()}")
# Check what methods are available
print("\n--- Available Methods ---")
print(f"Duck methods: swim(), fly(), walk(), describe()")
print(f"Penguin methods: swim(), walk(), describe()")
# Check inheritance
print("\n--- Inheritance Check (isinstance) ---")
print(f"duck is Swimmer: {isinstance(duck, Swimmer)}")
print(f"duck is Flyer: {isinstance(duck, Flyer)}")
print(f"duck is Walker: {isinstance(duck, Walker)}")
print(f"penguin is Swimmer: {isinstance(penguin, Swimmer)}")
print(f"penguin is Flyer: {isinstance(penguin, Flyer)}")
# Parent classes
print("\n--- Parent Classes (__bases__) ---")
print(f"Duck parents: {Duck.__bases__}")
print(f"Penguin parents: {Penguin.__bases__}")
print("\n=== Key Points ===")
print("""
1. class Child(Parent1, Parent2) inherits from both
2. Child has access to all parent methods
3. isinstance() checks all parent types
4. __bases__ shows direct parent classes
5. When methods conflict, leftmost parent wins
""")
main()
"""Can swim."""
3class Swimmer: #?swimmer_class4 """Can swim."""56 def swim(self): #?swim_method7 return f"{self.__class__.__name__} is swimming"89 def describe(self): #?swimmer_describe10 return "I can swim"111213class Flyer: #?flyer_class14 """Can fly."""1516 def fly(self): #?fly_method17 return f"{self.__class__.__name__} is flying"1819 def describe(self): #?flyer_describe20 return "I can fly"212223class Walker: #?walker_class24 """Can walk."""2526 def walk(self): #?walk_method27 return f"{self.__class__.__name__} is walking"2829 def describe(self): #?walker_describe30 return "I can walk"313233# Single inheritance - one parent #?single_inheritance34class Fish(Swimmer): #?fish_class35 pass363738# Multiple inheritance - two parents #?multiple_inheritance_239class Duck(Swimmer, Flyer, Walker): #?duck_class40 """Ducks can swim, fly, and walk!"""41 pass424344# Another multiple inheritance example #?penguin_example45class Penguin(Swimmer, Walker): #?penguin_class46 """Penguins can swim and walk, but not fly."""47 pass484950def main():51 print("=== Basic Multiple Inheritance ===\n")5253 # Single inheritance #?demo_single54 print("--- Single Inheritance (Fish) ---")55 fish = Fish() #?create_fish56 print(fish.swim()) #?fish_swim57 print(f"describe(): {fish.describe()}") #?fish_describe5859 # Multiple inheritance - Duck #?demo_duck60 print("\n--- Multiple Inheritance (Duck) ---")61 duck = Duck() #?create_duck62 print(duck.swim()) #?duck_swim63 print(duck.fly()) #?duck_fly64 print(duck.walk()) #?duck_walk65 print(f"describe(): {duck.describe()}") #?duck_describe_result6667 # Multiple inheritance - Penguin #?demo_penguin68 print("\n--- Multiple Inheritance (Penguin) ---")69 penguin = Penguin() #?create_penguin70 print(penguin.swim()) #?penguin_swim71 print(penguin.walk()) #?penguin_walk72 print(f"describe(): {penguin.describe()}") #?penguin_describe_result7374 # Check what methods are available #?check_methods75 print("\n--- Available Methods ---")76 print(f"Duck methods: swim(), fly(), walk(), describe()")77 print(f"Penguin methods: swim(), walk(), describe()")7879 # Check inheritance #?check_inheritance80 print("\n--- Inheritance Check (isinstance) ---")81 print(f"duck is Swimmer: {isinstance(duck, Swimmer)}") #?duck_is_swimmer82 print(f"duck is Flyer: {isinstance(duck, Flyer)}") #?duck_is_flyer83 print(f"duck is Walker: {isinstance(duck, Walker)}") #?duck_is_walker84 print(f"penguin is Swimmer: {isinstance(penguin, Swimmer)}") #?penguin_is_swimmer85 print(f"penguin is Flyer: {isinstance(penguin, Flyer)}") #?penguin_is_flyer8687 # Parent classes #?show_bases88 print("\n--- Parent Classes (__bases__) ---")89 print(f"Duck parents: {Duck.__bases__}") #?duck_bases90 print(f"Penguin parents: {Penguin.__bases__}") #?penguin_bases9192 print("\n=== Key Points ===")93 print("""94 1. class Child(Parent1, Parent2) inherits from both95 2. Child has access to all parent methods96 3. isinstance() checks all parent types97 4. __bases__ shows direct parent classes98 5. When methods conflict, leftmost parent wins99 """)100101main()fish ← ⟨Fish A⟩
50def main():51 print("=== Basic Multiple Inheritance ===\n")5253 # Single inheritance #?demo_single54 print("--- Single Inheritance (Fish) ---")55 fish→ ⟨Fish A⟩ = Fish() #?create_fish56 print(fish⟨Fish A⟩.swim()) #?fish_swim57 print(f"describe(): {fish.describe()}") #?fish_describeoutput=== Basic Multiple Inheritance === --- Single Inheritance (Fish) ---def swim(self): #?swim_method
pass 1 of 36def swim(self⟨Fish A⟩): #?swim_method7 return f"{self.__class__.__name__Fish} is swimming"All 3 passes — pass 1 is the card above pass selfself.__class__.__name__1 ⟨Fish A⟩ Fish 2 ⟨Duck B⟩ Duck 3 ⟨Penguin C⟩ Penguin print(fish.swim()) #?fish_swim
55fish = Fish() #?create_fish56print(fish⟨Fish A⟩.swim()) #?fish_swim57print(f"describe(): {fish⟨Fish A⟩.describe()}") #?fish_describeoutputFish is swimmingdef describe(self): #?swimmer_describe
pass 1 of 39def describe(self⟨Fish A⟩): #?swimmer_describe10 return "I can swim"All 3 passes — pass 1 is the card above pass self1 ⟨Fish A⟩ 2 ⟨Duck B⟩ 3 ⟨Penguin C⟩ duck ← ⟨Duck B⟩
56print(fish.swim()) #?fish_swim57print(f"describe(): {fish⟨Fish A⟩.describe()}") #?fish_describe5859# Multiple inheritance - Duck #?demo_duck60print("\n--- Multiple Inheritance (Duck) ---")61duck→ ⟨Duck B⟩ = Duck() #?create_duck62print(duck⟨Duck B⟩.swim()) #?duck_swim63print(duck.fly()) #?duck_flyoutputdescribe(): I can swim --- Multiple Inheritance (Duck) ---print(duck.swim()) #?duck_swim
61duck = Duck() #?create_duck62print(duck⟨Duck B⟩.swim()) #?duck_swim63print(duck⟨Duck B⟩.fly()) #?duck_fly64print(duck.walk()) #?duck_walkoutputDuck is swimmingdef fly(self): #?fly_method
16def fly(self⟨Duck B⟩): #?fly_method17 return f"{self.__class__.__name__Duck} is flying"print(duck.fly()) #?duck_fly
62print(duck.swim()) #?duck_swim63print(duck⟨Duck B⟩.fly()) #?duck_fly64print(duck⟨Duck B⟩.walk()) #?duck_walk65print(f"describe(): {duck.describe()}") #?duck_describe_resultoutputDuck is flyingdef walk(self): #?walk_method
pass 1 of 226def walk(self⟨Duck B⟩): #?walk_method27 return f"{self.__class__.__name__Duck} is walking"print(duck.walk()) #?duck_walk
63print(duck.fly()) #?duck_fly64print(duck⟨Duck B⟩.walk()) #?duck_walk65print(f"describe(): {duck⟨Duck B⟩.describe()}") #?duck_describe_resultoutputDuck is walkingpenguin ← ⟨Penguin C⟩
64print(duck.walk()) #?duck_walk65print(f"describe(): {duck⟨Duck B⟩.describe()}") #?duck_describe_result6667# Multiple inheritance - Penguin #?demo_penguin68print("\n--- Multiple Inheritance (Penguin) ---")69penguin→ ⟨Penguin C⟩ = Penguin() #?create_penguin70print(penguin⟨Penguin C⟩.swim()) #?penguin_swim71print(penguin.walk()) #?penguin_walkoutputdescribe(): I can swim --- Multiple Inheritance (Penguin) ---print(penguin.swim()) #?penguin_swim
69penguin = Penguin() #?create_penguin70print(penguin⟨Penguin C⟩.swim()) #?penguin_swim71print(penguin⟨Penguin C⟩.walk()) #?penguin_walk72print(f"describe(): {penguin.describe()}") #?penguin_describe_resultoutputPenguin is swimmingdef walk(self): #?walk_method
pass 2 of 226def walk(self⟨Penguin C⟩): #?walk_method27 return f"{self.__class__.__name__Penguin} is walking"print(penguin.walk()) #?penguin_walk
70print(penguin.swim()) #?penguin_swim71print(penguin⟨Penguin C⟩.walk()) #?penguin_walk72print(f"describe(): {penguin⟨Penguin C⟩.describe()}") #?penguin_describe_resultoutputPenguin is walkingprint(f"describe(): {penguin.describe()}") #?penguin_describe_result
71print(penguin.walk()) #?penguin_walk72print(f"describe(): {penguin⟨Penguin C⟩.describe()}") #?penguin_describe_result7374# Check what methods are available #?check_methods75print("\n--- Available Methods ---")76print(f"Duck methods: swim(), fly(), walk(), describe()")77print(f"Penguin methods: swim(), walk(), describe()")7879# Check inheritance #?check_inheritance80print("\n--- Inheritance Check (isinstance) ---")81print(f"duck is Swimmer: {isinstance(duck⟨Duck B⟩, Swimmer<class '__main__.Swimmer'>)}") #?duck_is_swimmer82print(f"duck is Flyer: {isinstance(duck⟨Duck B⟩, Flyer<class '__main__.Flyer'>)}") #?duck_is_flyer83print(f"duck is Walker: {isinstance(duck⟨Duck B⟩, Walker<class '__main__.Walker'>)}") #?duck_is_walker84print(f"penguin is Swimmer: {isinstance(penguin⟨Penguin C⟩, Swimmer<class '__main__.Swimmer'>)}") #?penguin_is_swimmer85print(f"penguin is Flyer: {isinstance(penguin⟨Penguin C⟩, Flyer<class '__main__.Flyer'>)}") #?penguin_is_flyer8687# Parent classes #?show_bases88print("\n--- Parent Classes (__bases__) ---")89print(f"Duck parents: {Duck.__bases__(<class '__main__.Swimmer'>, <class '__main__.Flyer'>, <class '__main__.Walker'>)}") #?duck_bases90print(f"Penguin parents: {Penguin.__bases__(<class '__main__.Swimmer'>, <class '__main__.Walker'>)}") #?penguin_bases9192print("\n=== Key Points ===")93print("""941. class Child(Parent1, Parent2) inherits from both952. Child has access to all parent methods963. isinstance() checks all parent types974. __bases__ shows direct parent classes985. When methods conflict, leftmost parent wins99""")outputdescribe(): I can swim --- Available Methods --- Duck methods: swim(), fly(), walk(), describe() Penguin methods: swim(), walk(), describe() --- Inheritance Check (isinstance) --- duck is Swimmer: True duck is Flyer: True duck is Walker: True penguin is Swimmer: True penguin is Flyer: False --- Parent Classes (__bases__) --- Duck parents: (<class '__main__.Swimmer'>, <class '__main__.Flyer'>, <class '__main__.Walker'>) Penguin parents: (<class '__main__.Swimmer'>, <class '__main__.Walker'>) === Key Points === 1. class Child(Parent1, Parent2) inherits from both 2. Child has access to all parent methods 3. isinstance() checks all parent types 4. __bases__ shows direct parent classes 5. When methods conflict, leftmost parent winsmain()
101main()
class Child(Parent1, Parent2): inherits from both. Child has all methods.
Method Resolution Order
How Python decides which method to call.
# Method Resolution Order (MRO)
class A:
def method(self):
return "A.method()"
def who_am_i(self):
return "I am A"
class B(A):
def method(self):
return "B.method()"
class C(A):
def method(self):
return "C.method()"
def who_am_i(self):
return "I am C"
class D(B, C):
pass # Inherits from both B and C
class E(C, B):
pass # Same parents, different order!
def main():
print("=== Method Resolution Order (MRO) ===\n")
# Display the inheritance structure
print("Inheritance Structure:")
print("""
A
/ \\
B C
\\ /
D
""")
# Check MRO for D
print("--- MRO for class D(B, C) ---")
print(f"D.__mro__ = {D.__mro__}")
print("\nMRO as list:")
for i, cls in enumerate(D.__mro__):
print(f" {i + 1}. {cls.__name__}")
# Method resolution demonstration
print("\n--- Method Resolution for D ---")
d = D()
print(f"d.method() = {d.method()}")
print(" → Found in B (first parent with method)")
print(f"d.who_am_i() = {d.who_am_i()}")
print(" → Not in D, not in B, found in C")
# Compare with E (different parent order)
print("\n--- MRO for class E(C, B) ---")
print(f"E.__mro__ = {E.__mro__}")
e = E()
print(f"\ne.method() = {e.method()}")
print(" → Found in C (first parent with method)")
print(f"e.who_am_i() = {e.who_am_i()}")
print(" → Found in C (first parent with method)")
# Using mro() method
print("\n--- Using mro() method ---")
print(f"D.mro() = {D.mro()}")
# MRO for simpler case
print("\n--- MRO for Simple Cases ---")
print(f"B.__mro__ = {B.__mro__}")
print(" B → A → object")
print(f"C.__mro__ = {C.__mro__}")
print(" C → A → object")
# Demonstrate search path
print("\n--- How Python Finds Methods ---")
print("When calling d.method():")
print(" 1. Look in D → Not found")
print(" 2. Look in B → FOUND! Stop here")
print(" 3. (Would look in C)")
print(" 4. (Would look in A)")
print(" 5. (Would look in object)")
print("\nWhen calling d.who_am_i():")
print(" 1. Look in D → Not found")
print(" 2. Look in B → Not found")
print(" 3. Look in C → FOUND! Stop here")
# Key points about MRO
print("\n=== C3 Linearization Rules ===")
print("""
1. Children come before parents
2. Left parents come before right parents
3. A class appears only once in MRO
4. Each class must appear before its parents
5. Follows C3 linearization algorithm
""")
main()
pass # Inherits from both B and C
24class D(B, C): #?class_d25 pass # Inherits from both B and C262728class E(C, B): #?class_e29 pass # Same parents, different order!303132def main():33 print("=== Method Resolution Order (MRO) ===\n")3435 # Display the inheritance structure #?show_structure36 print("Inheritance Structure:")37 print("""38 A39 / \\40 B C41 \\ /42 D43 """)4445 # Check MRO for D #?mro_d46 print("--- MRO for class D(B, C) ---")47 print(f"D.__mro__ = {D.__mro__}") #?d_mro_attr48 print("\nMRO as list:")49 for i, cls in enumerate(D.__mro__): #?loop_mro50 print(f" {i + 1}. {cls.__name__}") #?print_mro_item5152 # Method resolution demonstration #?method_resolution53 print("\n--- Method Resolution for D ---")54 d = D() #?create_d55 print(f"d.method() = {d.method()}") #?d_method_call56 print(" → Found in B (first parent with method)")5758 print(f"d.who_am_i() = {d.who_am_i()}") #?d_who_call59 print(" → Not in D, not in B, found in C")6061 # Compare with E (different parent order) #?compare_e62 print("\n--- MRO for class E(C, B) ---")63 print(f"E.__mro__ = {E.__mro__}") #?e_mro_attr6465 e = E() #?create_e66 print(f"\ne.method() = {e.method()}") #?e_method_call67 print(" → Found in C (first parent with method)")6869 print(f"e.who_am_i() = {e.who_am_i()}") #?e_who_call70 print(" → Found in C (first parent with method)")7172 # Using mro() method #?mro_method73 print("\n--- Using mro() method ---")74 print(f"D.mro() = {D.mro()}") #?mro_method_call7576 # MRO for simpler case #?simple_mro77 print("\n--- MRO for Simple Cases ---")78 print(f"B.__mro__ = {B.__mro__}") #?b_mro79 print(" B → A → object")8081 print(f"C.__mro__ = {C.__mro__}") #?c_mro82 print(" C → A → object")8384 # Demonstrate search path #?search_path85 print("\n--- How Python Finds Methods ---")86 print("When calling d.method():")87 print(" 1. Look in D → Not found")88 print(" 2. Look in B → FOUND! Stop here")89 print(" 3. (Would look in C)")90 print(" 4. (Would look in A)")91 print(" 5. (Would look in object)")9293 print("\nWhen calling d.who_am_i():")94 print(" 1. Look in D → Not found")95 print(" 2. Look in B → Not found")96 print(" 3. Look in C → FOUND! Stop here")9798 # Key points about MRO #?key_points99 print("\n=== C3 Linearization Rules ===")100 print("""101 1. Children come before parents102 2. Left parents come before right parents103 3. A class appears only once in MRO104 4. Each class must appear before its parents105 5. Follows C3 linearization algorithm106 """)107108main()def main():
32def main():33 print("=== Method Resolution Order (MRO) ===\n")3435 # Display the inheritance structure #?show_structure36 print("Inheritance Structure:")37 print("""38 A39 / \\40 B C41 \\ /42 D43 """)4445 # Check MRO for D #?mro_d46 print("--- MRO for class D(B, C) ---")47 print(f"D.__mro__ = {D.__mro__(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)}") #?d_mro_attr48 print("\nMRO as list:")49 for i, cls in enumerate(D.__mro__): #?loop_mrooutput=== Method Resolution Order (MRO) === Inheritance Structure: A / \ B C \ / D --- MRO for class D(B, C) --- D.__mro__ = (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) MRO as list:for i, cls in enumerate(D.__mro__): #?loop_mro
pass 1 of 548print("\nMRO as list:")49for i0, cls in enumerate(D.__mro__(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)): #?loop_mro50 print(f" {i0 + 1}. {cls.__name__D}") #?print_mro_itemoutput 1. DAll 5 passes — pass 1 is the card above pass icls.__name__1 0 D 2 1 B 3 2 C 4 3 A 5 4 object d ← ⟨D A⟩
52# Method resolution demonstration #?method_resolution53print("\n--- Method Resolution for D ---")54d→ ⟨D A⟩ = D() #?create_d55print(f"d.method() = {d⟨D A⟩.method()}") #?d_method_call56print(" → Found in B (first parent with method)")output --- Method Resolution for D ---def method(self): #?b_method
11class B(A): #?class_b12 def method(self⟨D A⟩): #?b_method13 return "B.method()"print(f"d.method() = {d.method()}") #?d_method_call
54d = D() #?create_d55print(f"d.method() = {d⟨D A⟩.method()}") #?d_method_call56print(" → Found in B (first parent with method)")5758print(f"d.who_am_i() = {d⟨D A⟩.who_am_i()}") #?d_who_call59print(" → Not in D, not in B, found in C")outputd.method() = B.method() → Found in B (first parent with method)def who_am_i(self): #?c_who
pass 1 of 220def who_am_i(self⟨D A⟩): #?c_who21 return "I am C"e ← ⟨E B⟩
58print(f"d.who_am_i() = {d⟨D A⟩.who_am_i()}") #?d_who_call59print(" → Not in D, not in B, found in C")6061# Compare with E (different parent order) #?compare_e62print("\n--- MRO for class E(C, B) ---")63print(f"E.__mro__ = {E.__mro__(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)}") #?e_mro_attr6465e→ ⟨E B⟩ = E() #?create_e66print(f"\ne.method() = {e⟨E B⟩.method()}") #?e_method_call67print(" → Found in C (first parent with method)")outputd.who_am_i() = I am C → Not in D, not in B, found in C --- MRO for class E(C, B) --- E.__mro__ = (<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)def method(self): #?c_method
16class C(A): #?class_c17 def method(self⟨E B⟩): #?c_method18 return "C.method()"print(f" e.method() = {e.method()}") #?e_method_call
65e = E() #?create_e66print(f"\ne.method() = {e⟨E B⟩.method()}") #?e_method_call67print(" → Found in C (first parent with method)")6869print(f"e.who_am_i() = {e⟨E B⟩.who_am_i()}") #?e_who_call70print(" → Found in C (first parent with method)")output e.method() = C.method() → Found in C (first parent with method)def who_am_i(self): #?c_who
pass 2 of 220def who_am_i(self⟨E B⟩): #?c_who21 return "I am C"print(f"e.who_am_i() = {e.who_am_i()}") #?e_who_call
69print(f"e.who_am_i() = {e⟨E B⟩.who_am_i()}") #?e_who_call70print(" → Found in C (first parent with method)")7172# Using mro() method #?mro_method73print("\n--- Using mro() method ---")74print(f"D.mro() = {D<class '__main__.D'>.mro()}") #?mro_method_call7576# MRO for simpler case #?simple_mro77print("\n--- MRO for Simple Cases ---")78print(f"B.__mro__ = {B.__mro__(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)}") #?b_mro79print(" B → A → object")8081print(f"C.__mro__ = {C.__mro__(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)}") #?c_mro82print(" C → A → object")8384# Demonstrate search path #?search_path85print("\n--- How Python Finds Methods ---")86print("When calling d.method():")87print(" 1. Look in D → Not found")88print(" 2. Look in B → FOUND! Stop here")89print(" 3. (Would look in C)")90print(" 4. (Would look in A)")91print(" 5. (Would look in object)")9293print("\nWhen calling d.who_am_i():")94print(" 1. Look in D → Not found")95print(" 2. Look in B → Not found")96print(" 3. Look in C → FOUND! Stop here")9798# Key points about MRO #?key_points99print("\n=== C3 Linearization Rules ===")100print("""1011. Children come before parents1022. Left parents come before right parents1033. A class appears only once in MRO1044. Each class must appear before its parents1055. Follows C3 linearization algorithm106""")outpute.who_am_i() = I am C → Found in C (first parent with method) --- Using mro() method --- D.mro() = [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] --- MRO for Simple Cases --- B.__mro__ = (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>) B → A → object C.__mro__ = (<class '__main__.C'>, <class '__main__.A'>, <class 'object'>) C → A → object --- How Python Finds Methods --- When calling d.method(): 1. Look in D → Not found 2. Look in B → FOUND! Stop here 3. (Would look in C) 4. (Would look in A) 5. (Would look in object) When calling d.who_am_i(): 1. Look in D → Not found 2. Look in B → Not found 3. Look in C → FOUND! Stop here === C3 Linearization Rules === 1. Children come before parents 2. Left parents come before right parents 3. A class appears only once in MRO 4. Each class must appear before its parents 5. Follows C3 linearization algorithmmain()
108main()
ClassName.__mro__ shows lookup order. Follows C3 linearization algorithm.
The diamond problem
When two parents share a grandparent.
# The Diamond Problem
# The "diamond" comes from the inheritance shape:
# A
# / \
# B C
# \ /
# D
class A:
def __init__(self):
print("A.__init__() called")
self.value = "from A"
def greet(self):
return "Hello from A"
def identify(self):
return f"A (value={self.value})"
class B(A):
def __init__(self):
print("B.__init__() called")
super().__init__() # Calls next in MRO
self.b_attr = "from B"
def greet(self):
return "Hello from B"
class C(A):
def __init__(self):
print("C.__init__() called")
super().__init__() # Calls next in MRO
self.c_attr = "from C"
def greet(self):
return "Hello from C"
class D(B, C):
def __init__(self):
print("D.__init__() called")
super().__init__() # Starts the chain
def main():
print("=== The Diamond Problem ===\n")
# Show the diamond structure
print("Diamond inheritance structure:")
print("""
A (base)
/ \\
B C
\\ /
D
""")
# The problem: A's __init__ called twice?
print("--- Without super() properly, A could be initialized twice ---")
print("With super() and MRO, A is initialized only ONCE!\n")
# Create D instance - watch the init order
print("Creating D():")
print("-" * 30)
d = D()
print("-" * 30)
# Show the MRO
print(f"\nD's MRO: {[cls.__name__ for cls in D.__mro__]}")
# Explain the init chain
print("\n--- Init Chain Explanation ---")
print("""
D.__init__() called
↓ super().__init__() → next in MRO is B
B.__init__() called
↓ super().__init__() → next in MRO is C (not A!)
C.__init__() called
↓ super().__init__() → next in MRO is A
A.__init__() called
✓ A is initialized only ONCE!
""")
# Check attributes
print("--- Attributes from all classes ---")
print(f"d.value = '{d.value}' (from A)")
print(f"d.b_attr = '{d.b_attr}' (from B)")
print(f"d.c_attr = '{d.c_attr}' (from C)")
# Method resolution in diamond
print("\n--- Method Resolution in Diamond ---")
print(f"d.greet() = '{d.greet()}'")
print(" → D has no greet(), check MRO")
print(" → B has greet() → 'Hello from B'")
print(f"\nd.identify() = '{d.identify()}'")
print(" → Only A has identify()")
# Bad approach: calling parent explicitly
print("\n--- Bad Approach (Don't Do This) ---")
print("""
# Instead of super(), calling parents directly:
class D(B, C):
def __init__(self):
B.__init__(self) # A initialized here
C.__init__(self) # A initialized AGAIN!
This would initialize A twice!
Always use super() in multiple inheritance.
""")
# Why super() works
print("=== Why super() Works ===")
print("""
1. super() follows MRO, not just parent
2. In D(B, C): super() in B calls C, not A
3. Each class is initialized exactly once
4. Order: D → B → C → A → object
5. This is called "cooperative multiple inheritance"
""")
main()
main()
125main()def main():
48def main():49 print("=== The Diamond Problem ===\n")5051 # Show the diamond structure #?show_diamond52 print("Diamond inheritance structure:")53 print("""54 A (base)55 / \\56 B C57 \\ /58 D59 """)6061 # The problem: A's __init__ called twice? #?the_problem62 print("--- Without super() properly, A could be initialized twice ---")63 print("With super() and MRO, A is initialized only ONCE!\n")6465 # Create D instance - watch the init order #?create_d66 print("Creating D():")67 print("-" * 30)68 d = D() #?d_instance69 print("-" * 30)output=== The Diamond Problem === Diamond inheritance structure: A (base) / \ B C \ / D --- Without super() properly, A could be initialized twice --- With super() and MRO, A is initialized only ONCE! Creating D(): ------------------------------def __init__(self): #?d_init
42class D(B, C): #?class_d43 def __init__(self⟨D A⟩): #?d_init44 print("D.__init__() called")45 super().__init__() # Starts the chainoutputD.__init__() calleddef __init__(self): #?b_init
22class B(A): #?class_b23 def __init__(self⟨D A⟩): #?b_init24 print("B.__init__() called")25 super().__init__() # Calls next in MROoutputB.__init__() calleddef __init__(self): #?c_init
32class C(A): #?class_c33 def __init__(self⟨D A⟩): #?c_init34 print("C.__init__() called")35 super().__init__() # Calls next in MROoutputC.__init__() calledself.value ← from A
10class A: #?class_a11 def __init__(self⟨D A⟩): #?a_init12 print("A.__init__() called")13 self.value→ from A = "from A"outputA.__init__() calledself.c_attr ← from C, self.b_attr ← from B
25 super().__init__() # Calls next in MRO26 self.b_attr→ from B = "from B"2728 def greet(self): #?b_greet29 return "Hello from B"303132class C(A): #?class_c33 def __init__(self): #?c_init34 print("C.__init__() called")35 super().__init__() # Calls next in MRO36 self.c_attr→ from C = "from C"d ← ⟨D A⟩
67print("-" * 30)68d→ ⟨D A⟩ = D() #?d_instance69print("-" * 30)7071# Show the MRO #?show_mro72print(f"\nD's MRO: {[cls.__name__(empty) for cls in D.__mro__(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)]}") #?d_mro7374# Explain the init chain #?explain_chain75print("\n--- Init Chain Explanation ---")76print("""77D.__init__() called78↓ super().__init__() → next in MRO is B79B.__init__() called80↓ super().__init__() → next in MRO is C (not A!)81C.__init__() called82↓ super().__init__() → next in MRO is A83A.__init__() called84✓ A is initialized only ONCE!85""")8687# Check attributes #?check_attrs88print("--- Attributes from all classes ---")89print(f"d.value = '{d.valuefrom A}' (from A)") #?d_value90print(f"d.b_attr = '{d.b_attrfrom B}' (from B)") #?d_b_attr91print(f"d.c_attr = '{d.c_attrfrom C}' (from C)") #?d_c_attr9293# Method resolution in diamond #?method_diamond94print("\n--- Method Resolution in Diamond ---")95print(f"d.greet() = '{d⟨D A⟩.greet()}'") #?d_greet96print(" → D has no greet(), check MRO")output------------------------------ D's MRO: ['D', 'B', 'C', 'A', 'object'] --- Init Chain Explanation --- D.__init__() called ↓ super().__init__() → next in MRO is B B.__init__() called ↓ super().__init__() → next in MRO is C (not A!) C.__init__() called ↓ super().__init__() → next in MRO is A A.__init__() called ✓ A is initialized only ONCE! --- Attributes from all classes --- d.value = 'from A' (from A) d.b_attr = 'from B' (from B) d.c_attr = 'from C' (from C) --- Method Resolution in Diamond ---def greet(self): #?b_greet
28def greet(self⟨D A⟩): #?b_greet29 return "Hello from B"print(f"d.greet() = '{d.greet()}'") #?d_greet
94print("\n--- Method Resolution in Diamond ---")95print(f"d.greet() = '{d⟨D A⟩.greet()}'") #?d_greet96print(" → D has no greet(), check MRO")97print(" → B has greet() → 'Hello from B'")9899print(f"\nd.identify() = '{d⟨D A⟩.identify()}'") #?d_identify100print(" → Only A has identify()")outputd.greet() = 'Hello from B' → D has no greet(), check MRO → B has greet() → 'Hello from B'def identify(self): #?a_identify
18def identify(self⟨D A⟩): #?a_identify19 return f"A (value={self.valuefrom A})"print(f" d.identify() = '{d.identify()}'") #?d_identify
99print(f"\nd.identify() = '{d⟨D A⟩.identify()}'") #?d_identify100print(" → Only A has identify()")101102# Bad approach: calling parent explicitly #?bad_approach103print("\n--- Bad Approach (Don't Do This) ---")104print("""105# Instead of super(), calling parents directly:106class D(B, C):107 def __init__(self):108 B.__init__(self) # A initialized here109 C.__init__(self) # A initialized AGAIN!110111This would initialize A twice!112Always use super() in multiple inheritance.113""")114115# Why super() works #?why_super116print("=== Why super() Works ===")117print("""1181. super() follows MRO, not just parent1192. In D(B, C): super() in B calls C, not A1203. Each class is initialized exactly once1214. Order: D → B → C → A → object1225. This is called "cooperative multiple inheritance"123""")output d.identify() = 'A (value=from A)' → Only A has identify() --- Bad Approach (Don't Do This) --- # Instead of super(), calling parents directly: class D(B, C): def __init__(self): B.__init__(self) # A initialized here C.__init__(self) # A initialized AGAIN! This would initialize A twice! Always use super() in multiple inheritance. === Why super() Works === 1. super() follows MRO, not just parent 2. In D(B, C): super() in B calls C, not A 3. Each class is initialized exactly once 4. Order: D → B → C → A → object 5. This is called "cooperative multiple inheritance"main()
125main()
A → B, C → D. Python's MRO ensures each class is called once.
super() chain
Cooperative multiple inheritance.
# Using super() with Multiple Inheritance
class Base:
def __init__(self, **kwargs):
print(f"Base.__init__(kwargs={kwargs})")
# End of chain - absorb remaining kwargs
def process(self):
print("Base.process()")
return ["Base"]
class FeatureA(Base):
def __init__(self, a_param="default_a", **kwargs):
print(f"FeatureA.__init__(a_param={a_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.a_param = a_param
def process(self):
print("FeatureA.process()")
result = super().process() # Call next in chain
return ["FeatureA"] + result
class FeatureB(Base):
def __init__(self, b_param="default_b", **kwargs):
print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.b_param = b_param
def process(self):
print("FeatureB.process()")
result = super().process() # Call next in chain
return ["FeatureB"] + result
class FeatureC(Base):
def __init__(self, c_param="default_c", **kwargs):
print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.c_param = c_param
def process(self):
print("FeatureC.process()")
result = super().process() # Call next in chain
return ["FeatureC"] + result
class Combined(FeatureA, FeatureB, FeatureC):
def __init__(self, **kwargs):
print(f"Combined.__init__(kwargs={kwargs})")
super().__init__(**kwargs) # Pass all kwargs
def process(self):
print("Combined.process()")
result = super().process() # Start the chain
return ["Combined"] + result
def main():
print("=== super() Chain with **kwargs ===\n")
# Show MRO
print("MRO for Combined:")
print([cls.__name__ for cls in Combined.__mro__])
print()
# Create with all parameters
print("--- Creating Combined with all parameters ---")
obj = Combined(a_param="A!", b_param="B!", c_param="C!")
print(f"\nAttributes:")
print(f" obj.a_param = '{obj.a_param}'")
print(f" obj.b_param = '{obj.b_param}'")
print(f" obj.c_param = '{obj.c_param}'")
# Process chain demonstration
print("\n--- Calling process() ---")
result = obj.process()
print(f"\nResult: {result}")
# Create with default parameters
print("\n" + "=" * 50)
print("--- Creating Combined with defaults ---")
obj2 = Combined()
print(f"\nAttributes (defaults):")
print(f" obj2.a_param = '{obj2.a_param}'")
print(f" obj2.b_param = '{obj2.b_param}'")
print(f" obj2.c_param = '{obj2.c_param}'")
# Explain the pattern
print("\n=== The **kwargs Pattern ===")
print("""
Each class:
1. Accepts its own named parameter
2. Accepts **kwargs for other classes
3. Uses super().__init__(**kwargs)
This lets each class extract its parameter
and pass the rest up the chain!
Example:
Combined(a_param="A", b_param="B", c_param="C")
→ Combined receives: {a_param="A", b_param="B", c_param="C"}
→ FeatureA takes a_param, passes: {b_param="B", c_param="C"}
→ FeatureB takes b_param, passes: {c_param="C"}
→ FeatureC takes c_param, passes: {}
→ Base receives: {} (empty)
""")
# super() with arguments
print("=== super() with Explicit Arguments ===")
print("""
# super() can take class and instance:
super(FeatureA, self).__init__(**kwargs)
# This is the same as:
super().__init__(**kwargs)
# In Python 3, super() automatically uses
# the enclosing class and first argument.
""")
main()
# Using super() with Multiple Inheritance
class Base:
def __init__(self, **kwargs):
print(f"Base.__init__(kwargs={kwargs})")
# End of chain - absorb remaining kwargs
def process(self):
print("Base.process()")
return ["Base"]
class FeatureA(Base):
def __init__(self, a_param="default_a", **kwargs):
print(f"FeatureA.__init__(a_param={a_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.a_param = a_param
def process(self):
print("FeatureA.process()")
result = super().process() # Call next in chain
return ["FeatureA"] + result
class FeatureB(Base):
def __init__(self, b_param="default_b", **kwargs):
print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.b_param = b_param
def process(self):
print("FeatureB.process()")
result = super().process() # Call next in chain
return ["FeatureB"] + result
class FeatureC(Base):
def __init__(self, c_param="default_c", **kwargs):
print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.c_param = c_param
def process(self):
print("FeatureC.process()")
result = super().process() # Call next in chain
return ["FeatureC"] + result
class Combined(FeatureA, FeatureB, FeatureC):
def __init__(self, **kwargs):
print(f"Combined.__init__(kwargs={kwargs})")
super().__init__(**kwargs) # Pass all kwargs
def process(self):
print("Combined.process()")
result = super().process() # Start the chain
return ["Combined"] + result
def main():
print("=== super() Chain with **kwargs ===\n")
# Show MRO
print("MRO for Combined:")
print([cls.__name__ for cls in Combined.__mro__])
print()
# Create with all parameters
print("--- Creating Combined with all parameters ---")
obj = Combined(a_param="alpha", b_param="beta", c_param="gamma")
print(f"\nAttributes:")
print(f" obj.a_param = '{obj.a_param}'")
print(f" obj.b_param = '{obj.b_param}'")
print(f" obj.c_param = '{obj.c_param}'")
# Process chain demonstration
print("\n--- Calling process() ---")
result = obj.process()
print(f"\nResult: {result}")
# Create with default parameters
print("\n" + "=" * 50)
print("--- Creating Combined with defaults ---")
obj2 = Combined()
print(f"\nAttributes (defaults):")
print(f" obj2.a_param = '{obj2.a_param}'")
print(f" obj2.b_param = '{obj2.b_param}'")
print(f" obj2.c_param = '{obj2.c_param}'")
# Explain the pattern
print("\n=== The **kwargs Pattern ===")
print("""
Each class:
1. Accepts its own named parameter
2. Accepts **kwargs for other classes
3. Uses super().__init__(**kwargs)
This lets each class extract its parameter
and pass the rest up the chain!
Example:
Combined(a_param="A", b_param="B", c_param="C")
→ Combined receives: {a_param="A", b_param="B", c_param="C"}
→ FeatureA takes a_param, passes: {b_param="B", c_param="C"}
→ FeatureB takes b_param, passes: {c_param="C"}
→ FeatureC takes c_param, passes: {}
→ Base receives: {} (empty)
""")
# super() with arguments
print("=== super() with Explicit Arguments ===")
print("""
# super() can take class and instance:
super(FeatureA, self).__init__(**kwargs)
# This is the same as:
super().__init__(**kwargs)
# In Python 3, super() automatically uses
# the enclosing class and first argument.
""")
main()
# Using super() with Multiple Inheritance
class Base:
def __init__(self, **kwargs):
print(f"Base.__init__(kwargs={kwargs})")
# End of chain - absorb remaining kwargs
def process(self):
print("Base.process()")
return ["Base"]
class FeatureA(Base):
def __init__(self, a_param="default_a", **kwargs):
print(f"FeatureA.__init__(a_param={a_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.a_param = a_param
def process(self):
print("FeatureA.process()")
result = super().process() # Call next in chain
return ["FeatureA"] + result
class FeatureB(Base):
def __init__(self, b_param="default_b", **kwargs):
print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.b_param = b_param
def process(self):
print("FeatureB.process()")
result = super().process() # Call next in chain
return ["FeatureB"] + result
class FeatureC(Base):
def __init__(self, c_param="default_c", **kwargs):
print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")
super().__init__(**kwargs) # Pass remaining kwargs up
self.c_param = c_param
def process(self):
print("FeatureC.process()")
result = super().process() # Call next in chain
return ["FeatureC"] + result
class Combined(FeatureA, FeatureB, FeatureC):
def __init__(self, **kwargs):
print(f"Combined.__init__(kwargs={kwargs})")
super().__init__(**kwargs) # Pass all kwargs
def process(self):
print("Combined.process()")
result = super().process() # Start the chain
return ["Combined"] + result
def main():
print("=== super() Chain with **kwargs ===\n")
# Show MRO
print("MRO for Combined:")
print([cls.__name__ for cls in Combined.__mro__])
print()
# Create with all parameters
print("--- Creating Combined with all parameters ---")
obj = Combined(a_param="left", b_param="middle", c_param="right")
print(f"\nAttributes:")
print(f" obj.a_param = '{obj.a_param}'")
print(f" obj.b_param = '{obj.b_param}'")
print(f" obj.c_param = '{obj.c_param}'")
# Process chain demonstration
print("\n--- Calling process() ---")
result = obj.process()
print(f"\nResult: {result}")
# Create with default parameters
print("\n" + "=" * 50)
print("--- Creating Combined with defaults ---")
obj2 = Combined()
print(f"\nAttributes (defaults):")
print(f" obj2.a_param = '{obj2.a_param}'")
print(f" obj2.b_param = '{obj2.b_param}'")
print(f" obj2.c_param = '{obj2.c_param}'")
# Explain the pattern
print("\n=== The **kwargs Pattern ===")
print("""
Each class:
1. Accepts its own named parameter
2. Accepts **kwargs for other classes
3. Uses super().__init__(**kwargs)
This lets each class extract its parameter
and pass the rest up the chain!
Example:
Combined(a_param="A", b_param="B", c_param="C")
→ Combined receives: {a_param="A", b_param="B", c_param="C"}
→ FeatureA takes a_param, passes: {b_param="B", c_param="C"}
→ FeatureB takes b_param, passes: {c_param="C"}
→ FeatureC takes c_param, passes: {}
→ Base receives: {} (empty)
""")
# super() with arguments
print("=== super() with Explicit Arguments ===")
print("""
# super() can take class and instance:
super(FeatureA, self).__init__(**kwargs)
# This is the same as:
super().__init__(**kwargs)
# In Python 3, super() automatically uses
# the enclosing class and first argument.
""")
main()
main()
127main()def main():
60def main():61 print("=== super() Chain with **kwargs ===\n")6263 # Show MRO #?show_mro64 print("MRO for Combined:")65 print([cls.__name__(empty) for cls in Combined.__mro__(<class '__main__.Combined'>, <class '__main__.FeatureA'>, <class '__main__.FeatureB'>, <class '__main__.FeatureC'>, <class '__main__.Base'>, <class 'object'>)]) #?print_mro66 print()6768 # Create with all parameters #?create_all69 print("--- Creating Combined with all parameters ---")70 obj = Combined(a_param="A!", b_param="B!", c_param="C!") #?combined_instance71 #@obj=Combined(a_param="alpha", b_param="beta", c_param="gamma"), Combined(a_param="left", b_param="middle", c_param="right")output=== super() Chain with **kwargs === MRO for Combined: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base', 'object'] --- Creating Combined with all parameters ---def __init__(self, **kwargs): #?combined_init
pass 1 of 249class Combined(FeatureA, FeatureB, FeatureC): #?combined_class50 def __init__(self⟨Combined A⟩, **kwargs): #?combined_init51 print(f"Combined.__init__(kwargs={kwargs{'a_param': 'A!', 'b_param': 'B!', 'c_param': 'C!'}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={'a_param': 'A!', 'b_param': 'B!', 'c_param': 'C!'})def __init__(self, a_param="default_a", **kwargs): #?feature_a_init
pass 1 of 213class FeatureA(Base): #?feature_a14 def __init__(self⟨Combined A⟩, a_paramA!="default_a", **kwargs): #?feature_a_init15 print(f"FeatureA.__init__(a_param={a_paramA!}, kwargs={kwargs{'b_param': 'B!', 'c_param': 'C!'}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=A!, kwargs={'b_param': 'B!', 'c_param': 'C!'})def __init__(self, b_param="default_b", **kwargs): #?feature_b_init
pass 1 of 225class FeatureB(Base): #?feature_b26 def __init__(self⟨Combined A⟩, b_paramB!="default_b", **kwargs): #?feature_b_init27 print(f"FeatureB.__init__(b_param={b_paramB!}, kwargs={kwargs{'c_param': 'C!'}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=B!, kwargs={'c_param': 'C!'})def __init__(self, c_param="default_c", **kwargs): #?feature_c_init
pass 1 of 237class FeatureC(Base): #?feature_c38 def __init__(self⟨Combined A⟩, c_paramC!="default_c", **kwargs): #?feature_c_init39 print(f"FeatureC.__init__(c_param={c_paramC!}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=C!, kwargs={})def __init__(self, **kwargs): #?base_init
pass 1 of 23class Base: #?base_class4 def __init__(self⟨Combined A⟩, **kwargs): #?base_init5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← C!, self.b_param ← B!, self.a_param ← A!
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ A! = a_paramA!1819 def process(self): #?feature_a_process20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base): #?feature_b26 def __init__(self, b_param="default_b", **kwargs): #?feature_b_init27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ B! = b_paramB!3031 def process(self): #?feature_b_process32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base): #?feature_c38 def __init__(self, c_param="default_c", **kwargs): #?feature_c_init39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ C! = c_paramC!obj ← ⟨Combined A⟩
69print("--- Creating Combined with all parameters ---")70obj→ ⟨Combined A⟩ = Combined(a_param="A!", b_param="B!", c_param="C!") #?combined_instance71#@obj=Combined(a_param="alpha", b_param="beta", c_param="gamma"), Combined(a_param="left", b_param="middle", c_param="right")7273print(f"\nAttributes:")74print(f" obj.a_param = '{obj.a_paramA!}'") #?check_a75print(f" obj.b_param = '{obj.b_paramB!}'") #?check_b76print(f" obj.c_param = '{obj.c_paramC!}'") #?check_c7778# Process chain demonstration #?process_demo79print("\n--- Calling process() ---")80result = obj⟨Combined A⟩.process() #?call_process81print(f"\nResult: {result}") #?print_resultoutput Attributes: obj.a_param = 'A!' obj.b_param = 'B!' obj.c_param = 'C!' --- Calling process() ---def process(self): #?combined_process
54def process(self⟨Combined A⟩): #?combined_process55 print("Combined.process()")56 result = super().process() # Start the chain57 return ["Combined"] + resultoutputCombined.process()def process(self): #?feature_a_process
19def process(self⟨Combined A⟩): #?feature_a_process20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + resultoutputFeatureA.process()def process(self): #?feature_b_process
31def process(self⟨Combined A⟩): #?feature_b_process32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + resultoutputFeatureB.process()def process(self): #?feature_c_process
43def process(self⟨Combined A⟩): #?feature_c_process44 print("FeatureC.process()")45 result = super().process() # Call next in chain46 return ["FeatureC"] + resultoutputFeatureC.process()def process(self): #?base_process
8def process(self⟨Combined A⟩): #?base_process9 print("Base.process()")10 return ["Base"]outputBase.process()result ← ['Base']
44print("FeatureC.process()")45result→ ['Base'] = super().process() # Call next in chain46return ["FeatureC"] + result['Base']result ← ['FeatureC', 'Base']
32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process() # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']result ← ['FeatureB', 'FeatureC', 'Base']
20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process() # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']
55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process() # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']result ← ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']
79print("\n--- Calling process() ---")80result→ ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] = obj⟨Combined A⟩.process() #?call_process81print(f"\nResult: {result['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']}") #?print_result8283# Create with default parameters #?create_default84print("\n" + "=" * 50)85print("--- Creating Combined with defaults ---")86obj2 = Combined() #?default_instanceoutput Result: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] ================================================== --- Creating Combined with defaults ---def __init__(self, **kwargs): #?combined_init
pass 2 of 249class Combined(FeatureA, FeatureB, FeatureC): #?combined_class50 def __init__(self⟨Combined B⟩, **kwargs): #?combined_init51 print(f"Combined.__init__(kwargs={kwargs{}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={})def __init__(self, a_param="default_a", **kwargs): #?feature_a_init
pass 2 of 213class FeatureA(Base): #?feature_a14 def __init__(self⟨Combined B⟩, a_paramdefault_a="default_a", **kwargs): #?feature_a_init15 print(f"FeatureA.__init__(a_param={a_paramdefault_a}, kwargs={kwargs{}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=default_a, kwargs={})def __init__(self, b_param="default_b", **kwargs): #?feature_b_init
pass 2 of 225class FeatureB(Base): #?feature_b26 def __init__(self⟨Combined B⟩, b_paramdefault_b="default_b", **kwargs): #?feature_b_init27 print(f"FeatureB.__init__(b_param={b_paramdefault_b}, kwargs={kwargs{}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=default_b, kwargs={})def __init__(self, c_param="default_c", **kwargs): #?feature_c_init
pass 2 of 237class FeatureC(Base): #?feature_c38 def __init__(self⟨Combined B⟩, c_paramdefault_c="default_c", **kwargs): #?feature_c_init39 print(f"FeatureC.__init__(c_param={c_paramdefault_c}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=default_c, kwargs={})def __init__(self, **kwargs): #?base_init
pass 2 of 23class Base: #?base_class4 def __init__(self⟨Combined B⟩, **kwargs): #?base_init5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← default_c, self.b_param ← default_b, self.a_param ← default_a
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ default_a = a_paramdefault_a1819 def process(self): #?feature_a_process20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base): #?feature_b26 def __init__(self, b_param="default_b", **kwargs): #?feature_b_init27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ default_b = b_paramdefault_b3031 def process(self): #?feature_b_process32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base): #?feature_c38 def __init__(self, c_param="default_c", **kwargs): #?feature_c_init39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ default_c = c_paramdefault_cobj2 ← ⟨Combined B⟩
85 print("--- Creating Combined with defaults ---")86 obj2→ ⟨Combined B⟩ = Combined() #?default_instance8788 print(f"\nAttributes (defaults):")89 print(f" obj2.a_param = '{obj2.a_paramdefault_a}'")90 print(f" obj2.b_param = '{obj2.b_paramdefault_b}'")91 print(f" obj2.c_param = '{obj2.c_paramdefault_c}'")9293 # Explain the pattern #?explain_pattern94 print("\n=== The **kwargs Pattern ===")95 print("""96 Each class:97 1. Accepts its own named parameter98 2. Accepts **kwargs for other classes99 3. Uses super().__init__(**kwargs)100101 This lets each class extract its parameter102 and pass the rest up the chain!103104 Example:105 Combined(a_param="A", b_param="B", c_param="C")106107 → Combined receives: {a_param="A", b_param="B", c_param="C"}108 → FeatureA takes a_param, passes: {b_param="B", c_param="C"}109 → FeatureB takes b_param, passes: {c_param="C"}110 → FeatureC takes c_param, passes: {}111 → Base receives: {} (empty)112 """)113114 # super() with arguments #?super_args115 print("=== super() with Explicit Arguments ===")116 print("""117 # super() can take class and instance:118 super(FeatureA, self).__init__(**kwargs)119120 # This is the same as:121 super().__init__(**kwargs)122123 # In Python 3, super() automatically uses124 # the enclosing class and first argument.125 """)126127main()output Attributes (defaults): obj2.a_param = 'default_a' obj2.b_param = 'default_b' obj2.c_param = 'default_c' === The **kwargs Pattern === Each class: 1. Accepts its own named parameter 2. Accepts **kwargs for other classes 3. Uses super().__init__(**kwargs) This lets each class extract its parameter and pass the rest up the chain! Example: Combined(a_param="A", b_param="B", c_param="C") → Combined receives: {a_param="A", b_param="B", c_param="C"} → FeatureA takes a_param, passes: {b_param="B", c_param="C"} → FeatureB takes b_param, passes: {c_param="C"} → FeatureC takes c_param, passes: {} → Base receives: {} (empty) === super() with Explicit Arguments === # super() can take class and instance: super(FeatureA, self).__init__(**kwargs) # This is the same as: super().__init__(**kwargs) # In Python 3, super() automatically uses # the enclosing class and first argument.
main()
126main()def main():
60def main():61 print("=== super() Chain with **kwargs ===\n")6263 # Show MRO64 print("MRO for Combined:")65 print([cls.__name__(empty) for cls in Combined.__mro__(<class '__main__.Combined'>, <class '__main__.FeatureA'>, <class '__main__.FeatureB'>, <class '__main__.FeatureC'>, <class '__main__.Base'>, <class 'object'>)])66 print()6768 # Create with all parameters69 print("--- Creating Combined with all parameters ---")70 obj = Combined(a_param="alpha", b_param="beta", c_param="gamma")output=== super() Chain with **kwargs === MRO for Combined: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base', 'object'] --- Creating Combined with all parameters ---def __init__(self, **kwargs):
pass 1 of 249class Combined(FeatureA, FeatureB, FeatureC):50 def __init__(self⟨Combined A⟩, **kwargs):51 print(f"Combined.__init__(kwargs={kwargs{'a_param': 'alpha', 'b_param': 'beta', 'c_param': 'gamma'}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={'a_param': 'alpha', 'b_param': 'beta', 'c_param': 'gamma'})def __init__(self, a_param="default_a", **kwargs):
pass 1 of 213class FeatureA(Base):14 def __init__(self⟨Combined A⟩, a_paramalpha="default_a", **kwargs):15 print(f"FeatureA.__init__(a_param={a_paramalpha}, kwargs={kwargs{'b_param': 'beta', 'c_param': 'gamma'}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=alpha, kwargs={'b_param': 'beta', 'c_param': 'gamma'})def __init__(self, b_param="default_b", **kwargs):
pass 1 of 225class FeatureB(Base):26 def __init__(self⟨Combined A⟩, b_parambeta="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_parambeta}, kwargs={kwargs{'c_param': 'gamma'}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=beta, kwargs={'c_param': 'gamma'})def __init__(self, c_param="default_c", **kwargs):
pass 1 of 237class FeatureC(Base):38 def __init__(self⟨Combined A⟩, c_paramgamma="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_paramgamma}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=gamma, kwargs={})def __init__(self, **kwargs):
pass 1 of 23class Base:4 def __init__(self⟨Combined A⟩, **kwargs):5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← gamma, self.b_param ← beta, self.a_param ← alpha
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ alpha = a_paramalpha1819 def process(self):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base):26 def __init__(self, b_param="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ beta = b_parambeta3031 def process(self):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base):38 def __init__(self, c_param="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ gamma = c_paramgammaobj ← ⟨Combined A⟩
69print("--- Creating Combined with all parameters ---")70obj→ ⟨Combined A⟩ = Combined(a_param="alpha", b_param="beta", c_param="gamma")7172print(f"\nAttributes:")73print(f" obj.a_param = '{obj.a_paramalpha}'")74print(f" obj.b_param = '{obj.b_parambeta}'")75print(f" obj.c_param = '{obj.c_paramgamma}'")7677# Process chain demonstration78print("\n--- Calling process() ---")79result = obj⟨Combined A⟩.process()80print(f"\nResult: {result}")output Attributes: obj.a_param = 'alpha' obj.b_param = 'beta' obj.c_param = 'gamma' --- Calling process() ---def process(self):
54def process(self⟨Combined A⟩):55 print("Combined.process()")56 result = super().process() # Start the chain57 return ["Combined"] + resultoutputCombined.process()def process(self):
19def process(self⟨Combined A⟩):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + resultoutputFeatureA.process()def process(self):
31def process(self⟨Combined A⟩):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + resultoutputFeatureB.process()def process(self):
43def process(self⟨Combined A⟩):44 print("FeatureC.process()")45 result = super().process() # Call next in chain46 return ["FeatureC"] + resultoutputFeatureC.process()def process(self):
8def process(self⟨Combined A⟩):9 print("Base.process()")10 return ["Base"]outputBase.process()result ← ['Base']
44print("FeatureC.process()")45result→ ['Base'] = super().process() # Call next in chain46return ["FeatureC"] + result['Base']result ← ['FeatureC', 'Base']
32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process() # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']result ← ['FeatureB', 'FeatureC', 'Base']
20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process() # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']
55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process() # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']result ← ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']
78print("\n--- Calling process() ---")79result→ ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] = obj⟨Combined A⟩.process()80print(f"\nResult: {result['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']}")8182# Create with default parameters83print("\n" + "=" * 50)84print("--- Creating Combined with defaults ---")85obj2 = Combined()output Result: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] ================================================== --- Creating Combined with defaults ---def __init__(self, **kwargs):
pass 2 of 249class Combined(FeatureA, FeatureB, FeatureC):50 def __init__(self⟨Combined B⟩, **kwargs):51 print(f"Combined.__init__(kwargs={kwargs{}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={})def __init__(self, a_param="default_a", **kwargs):
pass 2 of 213class FeatureA(Base):14 def __init__(self⟨Combined B⟩, a_paramdefault_a="default_a", **kwargs):15 print(f"FeatureA.__init__(a_param={a_paramdefault_a}, kwargs={kwargs{}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=default_a, kwargs={})def __init__(self, b_param="default_b", **kwargs):
pass 2 of 225class FeatureB(Base):26 def __init__(self⟨Combined B⟩, b_paramdefault_b="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_paramdefault_b}, kwargs={kwargs{}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=default_b, kwargs={})def __init__(self, c_param="default_c", **kwargs):
pass 2 of 237class FeatureC(Base):38 def __init__(self⟨Combined B⟩, c_paramdefault_c="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_paramdefault_c}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=default_c, kwargs={})def __init__(self, **kwargs):
pass 2 of 23class Base:4 def __init__(self⟨Combined B⟩, **kwargs):5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← default_c, self.b_param ← default_b, self.a_param ← default_a
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ default_a = a_paramdefault_a1819 def process(self):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base):26 def __init__(self, b_param="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ default_b = b_paramdefault_b3031 def process(self):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base):38 def __init__(self, c_param="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ default_c = c_paramdefault_cobj2 ← ⟨Combined B⟩
84 print("--- Creating Combined with defaults ---")85 obj2→ ⟨Combined B⟩ = Combined()8687 print(f"\nAttributes (defaults):")88 print(f" obj2.a_param = '{obj2.a_paramdefault_a}'")89 print(f" obj2.b_param = '{obj2.b_paramdefault_b}'")90 print(f" obj2.c_param = '{obj2.c_paramdefault_c}'")9192 # Explain the pattern93 print("\n=== The **kwargs Pattern ===")94 print("""95 Each class:96 1. Accepts its own named parameter97 2. Accepts **kwargs for other classes98 3. Uses super().__init__(**kwargs)99100 This lets each class extract its parameter101 and pass the rest up the chain!102103 Example:104 Combined(a_param="A", b_param="B", c_param="C")105106 → Combined receives: {a_param="A", b_param="B", c_param="C"}107 → FeatureA takes a_param, passes: {b_param="B", c_param="C"}108 → FeatureB takes b_param, passes: {c_param="C"}109 → FeatureC takes c_param, passes: {}110 → Base receives: {} (empty)111 """)112113 # super() with arguments114 print("=== super() with Explicit Arguments ===")115 print("""116 # super() can take class and instance:117 super(FeatureA, self).__init__(**kwargs)118119 # This is the same as:120 super().__init__(**kwargs)121122 # In Python 3, super() automatically uses123 # the enclosing class and first argument.124 """)125126main()output Attributes (defaults): obj2.a_param = 'default_a' obj2.b_param = 'default_b' obj2.c_param = 'default_c' === The **kwargs Pattern === Each class: 1. Accepts its own named parameter 2. Accepts **kwargs for other classes 3. Uses super().__init__(**kwargs) This lets each class extract its parameter and pass the rest up the chain! Example: Combined(a_param="A", b_param="B", c_param="C") → Combined receives: {a_param="A", b_param="B", c_param="C"} → FeatureA takes a_param, passes: {b_param="B", c_param="C"} → FeatureB takes b_param, passes: {c_param="C"} → FeatureC takes c_param, passes: {} → Base receives: {} (empty) === super() with Explicit Arguments === # super() can take class and instance: super(FeatureA, self).__init__(**kwargs) # This is the same as: super().__init__(**kwargs) # In Python 3, super() automatically uses # the enclosing class and first argument.
main()
126main()def main():
60def main():61 print("=== super() Chain with **kwargs ===\n")6263 # Show MRO64 print("MRO for Combined:")65 print([cls.__name__(empty) for cls in Combined.__mro__(<class '__main__.Combined'>, <class '__main__.FeatureA'>, <class '__main__.FeatureB'>, <class '__main__.FeatureC'>, <class '__main__.Base'>, <class 'object'>)])66 print()6768 # Create with all parameters69 print("--- Creating Combined with all parameters ---")70 obj = Combined(a_param="left", b_param="middle", c_param="right")output=== super() Chain with **kwargs === MRO for Combined: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base', 'object'] --- Creating Combined with all parameters ---def __init__(self, **kwargs):
pass 1 of 249class Combined(FeatureA, FeatureB, FeatureC):50 def __init__(self⟨Combined A⟩, **kwargs):51 print(f"Combined.__init__(kwargs={kwargs{'a_param': 'left', 'b_param': 'middle', 'c_param': 'right'}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={'a_param': 'left', 'b_param': 'middle', 'c_param': 'right'})def __init__(self, a_param="default_a", **kwargs):
pass 1 of 213class FeatureA(Base):14 def __init__(self⟨Combined A⟩, a_paramleft="default_a", **kwargs):15 print(f"FeatureA.__init__(a_param={a_paramleft}, kwargs={kwargs{'b_param': 'middle', 'c_param': 'right'}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=left, kwargs={'b_param': 'middle', 'c_param': 'right'})def __init__(self, b_param="default_b", **kwargs):
pass 1 of 225class FeatureB(Base):26 def __init__(self⟨Combined A⟩, b_parammiddle="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_parammiddle}, kwargs={kwargs{'c_param': 'right'}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=middle, kwargs={'c_param': 'right'})def __init__(self, c_param="default_c", **kwargs):
pass 1 of 237class FeatureC(Base):38 def __init__(self⟨Combined A⟩, c_paramright="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_paramright}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=right, kwargs={})def __init__(self, **kwargs):
pass 1 of 23class Base:4 def __init__(self⟨Combined A⟩, **kwargs):5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← right, self.b_param ← middle, self.a_param ← left
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ left = a_paramleft1819 def process(self):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base):26 def __init__(self, b_param="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ middle = b_parammiddle3031 def process(self):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base):38 def __init__(self, c_param="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ right = c_paramrightobj ← ⟨Combined A⟩
69print("--- Creating Combined with all parameters ---")70obj→ ⟨Combined A⟩ = Combined(a_param="left", b_param="middle", c_param="right")7172print(f"\nAttributes:")73print(f" obj.a_param = '{obj.a_paramleft}'")74print(f" obj.b_param = '{obj.b_parammiddle}'")75print(f" obj.c_param = '{obj.c_paramright}'")7677# Process chain demonstration78print("\n--- Calling process() ---")79result = obj⟨Combined A⟩.process()80print(f"\nResult: {result}")output Attributes: obj.a_param = 'left' obj.b_param = 'middle' obj.c_param = 'right' --- Calling process() ---def process(self):
54def process(self⟨Combined A⟩):55 print("Combined.process()")56 result = super().process() # Start the chain57 return ["Combined"] + resultoutputCombined.process()def process(self):
19def process(self⟨Combined A⟩):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + resultoutputFeatureA.process()def process(self):
31def process(self⟨Combined A⟩):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + resultoutputFeatureB.process()def process(self):
43def process(self⟨Combined A⟩):44 print("FeatureC.process()")45 result = super().process() # Call next in chain46 return ["FeatureC"] + resultoutputFeatureC.process()def process(self):
8def process(self⟨Combined A⟩):9 print("Base.process()")10 return ["Base"]outputBase.process()result ← ['Base']
44print("FeatureC.process()")45result→ ['Base'] = super().process() # Call next in chain46return ["FeatureC"] + result['Base']result ← ['FeatureC', 'Base']
32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process() # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']result ← ['FeatureB', 'FeatureC', 'Base']
20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process() # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']
55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process() # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']result ← ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']
78print("\n--- Calling process() ---")79result→ ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] = obj⟨Combined A⟩.process()80print(f"\nResult: {result['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']}")8182# Create with default parameters83print("\n" + "=" * 50)84print("--- Creating Combined with defaults ---")85obj2 = Combined()output Result: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base'] ================================================== --- Creating Combined with defaults ---def __init__(self, **kwargs):
pass 2 of 249class Combined(FeatureA, FeatureB, FeatureC):50 def __init__(self⟨Combined B⟩, **kwargs):51 print(f"Combined.__init__(kwargs={kwargs{}})")52 super().__init__(**kwargs) # Pass all kwargsoutputCombined.__init__(kwargs={})def __init__(self, a_param="default_a", **kwargs):
pass 2 of 213class FeatureA(Base):14 def __init__(self⟨Combined B⟩, a_paramdefault_a="default_a", **kwargs):15 print(f"FeatureA.__init__(a_param={a_paramdefault_a}, kwargs={kwargs{}})")16 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureA.__init__(a_param=default_a, kwargs={})def __init__(self, b_param="default_b", **kwargs):
pass 2 of 225class FeatureB(Base):26 def __init__(self⟨Combined B⟩, b_paramdefault_b="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_paramdefault_b}, kwargs={kwargs{}})")28 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureB.__init__(b_param=default_b, kwargs={})def __init__(self, c_param="default_c", **kwargs):
pass 2 of 237class FeatureC(Base):38 def __init__(self⟨Combined B⟩, c_paramdefault_c="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_paramdefault_c}, kwargs={kwargs{}})")40 super().__init__(**kwargs) # Pass remaining kwargs upoutputFeatureC.__init__(c_param=default_c, kwargs={})def __init__(self, **kwargs):
pass 2 of 23class Base:4 def __init__(self⟨Combined B⟩, **kwargs):5 print(f"Base.__init__(kwargs={kwargs{}})")6 # End of chain - absorb remaining kwargsoutputBase.__init__(kwargs={})self.c_param ← default_c, self.b_param ← default_b, self.a_param ← default_a
16 super().__init__(**kwargs) # Pass remaining kwargs up17 self.a_param→ default_a = a_paramdefault_a1819 def process(self):20 print("FeatureA.process()")21 result = super().process() # Call next in chain22 return ["FeatureA"] + result232425class FeatureB(Base):26 def __init__(self, b_param="default_b", **kwargs):27 print(f"FeatureB.__init__(b_param={b_param}, kwargs={kwargs})")28 super().__init__(**kwargs) # Pass remaining kwargs up29 self.b_param→ default_b = b_paramdefault_b3031 def process(self):32 print("FeatureB.process()")33 result = super().process() # Call next in chain34 return ["FeatureB"] + result353637class FeatureC(Base):38 def __init__(self, c_param="default_c", **kwargs):39 print(f"FeatureC.__init__(c_param={c_param}, kwargs={kwargs})")40 super().__init__(**kwargs) # Pass remaining kwargs up41 self.c_param→ default_c = c_paramdefault_cobj2 ← ⟨Combined B⟩
84 print("--- Creating Combined with defaults ---")85 obj2→ ⟨Combined B⟩ = Combined()8687 print(f"\nAttributes (defaults):")88 print(f" obj2.a_param = '{obj2.a_paramdefault_a}'")89 print(f" obj2.b_param = '{obj2.b_paramdefault_b}'")90 print(f" obj2.c_param = '{obj2.c_paramdefault_c}'")9192 # Explain the pattern93 print("\n=== The **kwargs Pattern ===")94 print("""95 Each class:96 1. Accepts its own named parameter97 2. Accepts **kwargs for other classes98 3. Uses super().__init__(**kwargs)99100 This lets each class extract its parameter101 and pass the rest up the chain!102103 Example:104 Combined(a_param="A", b_param="B", c_param="C")105106 → Combined receives: {a_param="A", b_param="B", c_param="C"}107 → FeatureA takes a_param, passes: {b_param="B", c_param="C"}108 → FeatureB takes b_param, passes: {c_param="C"}109 → FeatureC takes c_param, passes: {}110 → Base receives: {} (empty)111 """)112113 # super() with arguments114 print("=== super() with Explicit Arguments ===")115 print("""116 # super() can take class and instance:117 super(FeatureA, self).__init__(**kwargs)118119 # This is the same as:120 super().__init__(**kwargs)121122 # In Python 3, super() automatically uses123 # the enclosing class and first argument.124 """)125126main()output Attributes (defaults): obj2.a_param = 'default_a' obj2.b_param = 'default_b' obj2.c_param = 'default_c' === The **kwargs Pattern === Each class: 1. Accepts its own named parameter 2. Accepts **kwargs for other classes 3. Uses super().__init__(**kwargs) This lets each class extract its parameter and pass the rest up the chain! Example: Combined(a_param="A", b_param="B", c_param="C") → Combined receives: {a_param="A", b_param="B", c_param="C"} → FeatureA takes a_param, passes: {b_param="B", c_param="C"} → FeatureB takes b_param, passes: {c_param="C"} → FeatureC takes c_param, passes: {} → Base receives: {} (empty) === super() with Explicit Arguments === # super() can take class and instance: super(FeatureA, self).__init__(**kwargs) # This is the same as: super().__init__(**kwargs) # In Python 3, super() automatically uses # the enclosing class and first argument.
super() follows MRO, not direct parent. Essential for diamond pattern.
Conflict resolution
What happens when parents have same method.
# Handling Method Name Conflicts
class Logger:
def log(self, message):
return f"[LOG] {message}"
def get_info(self):
return "Logger: Provides logging capability"
class Serializer:
def serialize(self):
return f"<{self.__class__.__name__}/>"
def get_info(self):
return "Serializer: Provides serialization"
class Validator:
def validate(self, data):
return len(data) > 0
def get_info(self):
return "Validator: Provides validation"
# Method name conflict: all have get_info()
class DataProcessor(Logger, Serializer, Validator):
def process(self, data):
if self.validate(data):
self.log(f"Processing: {data}")
return self.serialize()
return None
# Solution 1: Override and choose
class ProcessorV1(Logger, Serializer, Validator):
def get_info(self):
# Explicitly choose Logger's version
return Logger.get_info(self)
# Solution 2: Override and combine
class ProcessorV2(Logger, Serializer, Validator):
def get_info(self):
# Combine all parent info
info_parts = [
Logger.get_info(self),
Serializer.get_info(self),
Validator.get_info(self),
]
return " | ".join(info_parts)
# Solution 3: Use super() chain
class LoggerChain:
def get_info(self):
info = "Logger: Provides logging"
parent_info = super().get_info() if hasattr(super(), 'get_info') else ""
return info + (" | " + parent_info if parent_info else "")
class SerializerChain:
def get_info(self):
info = "Serializer: Provides serialization"
parent_info = super().get_info() if hasattr(super(), 'get_info') else ""
return info + (" | " + parent_info if parent_info else "")
class ValidatorChain:
def get_info(self):
info = "Validator: Provides validation"
parent_info = super().get_info() if hasattr(super(), 'get_info') else ""
return info + (" | " + parent_info if parent_info else "")
class Base:
def get_info(self):
return "" # End of chain
class ProcessorV3(LoggerChain, SerializerChain, ValidatorChain, Base):
pass
def main():
print("=== Handling Method Name Conflicts ===\n")
# Show the conflict
print("--- The Conflict ---")
print("Logger, Serializer, and Validator all have get_info()")
print("When a class inherits from all three, which one wins?\n")
# Default behavior: leftmost wins
print("--- Default Behavior (Leftmost Wins) ---")
proc = DataProcessor()
print(f"MRO: {[c.__name__ for c in DataProcessor.__mro__]}")
print(f"proc.get_info() = '{proc.get_info()}'")
print(" → Logger's version (first in inheritance list)")
# Solution 1: Explicitly choose
print("\n--- Solution 1: Explicitly Choose ---")
v1 = ProcessorV1()
print(f"v1.get_info() = '{v1.get_info()}'")
print(" → Override calls Logger.get_info(self) directly")
# Solution 2: Combine all
print("\n--- Solution 2: Combine All ---")
v2 = ProcessorV2()
print(f"v2.get_info() = '{v2.get_info()}'")
print(" → Override calls all three parent methods")
# Solution 3: super() chain
print("\n--- Solution 3: super() Chain ---")
v3 = ProcessorV3()
print(f"MRO: {[c.__name__ for c in ProcessorV3.__mro__]}")
print(f"v3.get_info() = '{v3.get_info()}'")
print(" → Each class calls super().get_info() to chain")
# Accessing specific parent methods
print("\n--- Accessing Specific Parent Methods ---")
print(f"Logger.get_info(proc) = '{Logger.get_info(proc)}'")
print(f"Serializer.get_info(proc) = '{Serializer.get_info(proc)}'")
print(f"Validator.get_info(proc) = '{Validator.get_info(proc)}'")
print(" → Can always call parent method directly with instance")
# Best practices
print("\n=== Best Practices for Conflicts ===")
print("""
1. Avoid conflicts by using unique method names
2. If conflict unavoidable, override in child
3. Use ParentClass.method(self) for explicit calls
4. Design parent classes to work with super() chain
5. Document which parent's method is used
6. Consider composition over inheritance
""")
main()
pass
82class ProcessorV3(LoggerChain, SerializerChain, ValidatorChain, Base): #?processor_v383 pass848586def main():87 print("=== Handling Method Name Conflicts ===\n")8889 # Show the conflict #?show_conflict90 print("--- The Conflict ---")91 print("Logger, Serializer, and Validator all have get_info()")92 print("When a class inherits from all three, which one wins?\n")9394 # Default behavior: leftmost wins #?default_behavior95 print("--- Default Behavior (Leftmost Wins) ---")96 proc = DataProcessor() #?create_processor97 print(f"MRO: {[c.__name__ for c in DataProcessor.__mro__]}") #?proc_mro98 print(f"proc.get_info() = '{proc.get_info()}'") #?proc_get_info99 print(" → Logger's version (first in inheritance list)")100101 # Solution 1: Explicitly choose #?demo_v1102 print("\n--- Solution 1: Explicitly Choose ---")103 v1 = ProcessorV1() #?create_v1104 print(f"v1.get_info() = '{v1.get_info()}'") #?v1_result105 print(" → Override calls Logger.get_info(self) directly")106107 # Solution 2: Combine all #?demo_v2108 print("\n--- Solution 2: Combine All ---")109 v2 = ProcessorV2() #?create_v2110 print(f"v2.get_info() = '{v2.get_info()}'") #?v2_result111 print(" → Override calls all three parent methods")112113 # Solution 3: super() chain #?demo_v3114 print("\n--- Solution 3: super() Chain ---")115 v3 = ProcessorV3() #?create_v3116 print(f"MRO: {[c.__name__ for c in ProcessorV3.__mro__]}") #?v3_mro117 print(f"v3.get_info() = '{v3.get_info()}'") #?v3_result118 print(" → Each class calls super().get_info() to chain")119120 # Accessing specific parent methods #?access_specific121 print("\n--- Accessing Specific Parent Methods ---")122 print(f"Logger.get_info(proc) = '{Logger.get_info(proc)}'") #?call_logger123 print(f"Serializer.get_info(proc) = '{Serializer.get_info(proc)}'") #?call_serializer124 print(f"Validator.get_info(proc) = '{Validator.get_info(proc)}'") #?call_validator125 print(" → Can always call parent method directly with instance")126127 # Best practices #?best_practices128 print("\n=== Best Practices for Conflicts ===")129 print("""130 1. Avoid conflicts by using unique method names131 2. If conflict unavoidable, override in child132 3. Use ParentClass.method(self) for explicit calls133 4. Design parent classes to work with super() chain134 5. Document which parent's method is used135 6. Consider composition over inheritance136 """)137138main()proc ← ⟨DataProcessor A⟩
86def main():87 print("=== Handling Method Name Conflicts ===\n")8889 # Show the conflict #?show_conflict90 print("--- The Conflict ---")91 print("Logger, Serializer, and Validator all have get_info()")92 print("When a class inherits from all three, which one wins?\n")9394 # Default behavior: leftmost wins #?default_behavior95 print("--- Default Behavior (Leftmost Wins) ---")96 proc→ ⟨DataProcessor A⟩ = DataProcessor() #?create_processor97 print(f"MRO: {[c.__name__(empty) for c in DataProcessor.__mro__(<class '__main__.DataProcessor'>, <class '__main__.Logger'>, <class '__main__.Serializer'>, <class '__main__.Validator'>, <class 'object'>)]}") #?proc_mro98 print(f"proc.get_info() = '{proc⟨DataProcessor A⟩.get_info()}'") #?proc_get_info99 print(" → Logger's version (first in inheritance list)")output=== Handling Method Name Conflicts === --- The Conflict --- Logger, Serializer, and Validator all have get_info() When a class inherits from all three, which one wins? --- Default Behavior (Leftmost Wins) --- MRO: ['DataProcessor', 'Logger', 'Serializer', 'Validator', 'object']def get_info(self): #?logger_info
pass 1 of 47def get_info(self⟨DataProcessor A⟩): #?logger_info8 return "Logger: Provides logging capability"All 4 passes — pass 1 is the card above pass self1 ⟨DataProcessor A⟩ 2 ⟨ProcessorV1 B⟩ 3 ⟨ProcessorV2 C⟩ 4 ⟨DataProcessor A⟩ v1 ← ⟨ProcessorV1 B⟩
97print(f"MRO: {[c.__name__ for c in DataProcessor.__mro__]}") #?proc_mro98print(f"proc.get_info() = '{proc⟨DataProcessor A⟩.get_info()}'") #?proc_get_info99print(" → Logger's version (first in inheritance list)")100101# Solution 1: Explicitly choose #?demo_v1102print("\n--- Solution 1: Explicitly Choose ---")103v1→ ⟨ProcessorV1 B⟩ = ProcessorV1() #?create_v1104print(f"v1.get_info() = '{v1⟨ProcessorV1 B⟩.get_info()}'") #?v1_result105print(" → Override calls Logger.get_info(self) directly")outputproc.get_info() = 'Logger: Provides logging capability' → Logger's version (first in inheritance list) --- Solution 1: Explicitly Choose ---def get_info(self): #?v1_get_info # Explicitly choose Logger's…
37class ProcessorV1(Logger, Serializer, Validator): #?processor_v138 def get_info(self⟨ProcessorV1 B⟩): #?v1_get_info39 # Explicitly choose Logger's version40 return Logger<class '__main__.Logger'>.get_info(self)v2 ← ⟨ProcessorV2 C⟩
103v1 = ProcessorV1() #?create_v1104print(f"v1.get_info() = '{v1⟨ProcessorV1 B⟩.get_info()}'") #?v1_result105print(" → Override calls Logger.get_info(self) directly")106107# Solution 2: Combine all #?demo_v2108print("\n--- Solution 2: Combine All ---")109v2→ ⟨ProcessorV2 C⟩ = ProcessorV2() #?create_v2110print(f"v2.get_info() = '{v2⟨ProcessorV2 C⟩.get_info()}'") #?v2_result111print(" → Override calls all three parent methods")outputv1.get_info() = 'Logger: Provides logging capability' → Override calls Logger.get_info(self) directly --- Solution 2: Combine All ---def get_info(self): #?v2_get_info # Combine all parent info
44class ProcessorV2(Logger, Serializer, Validator): #?processor_v245 def get_info(self⟨ProcessorV2 C⟩): #?v2_get_info46 # Combine all parent info47 info_parts = [48 Logger<class '__main__.Logger'>.get_info(self),49 Serializer<class '__main__.Serializer'>.get_info(self),50 Validator<class '__main__.Validator'>.get_info(self),51 ]52 return " | ".join(info_parts)def get_info(self): #?serializer_info
pass 1 of 215def get_info(self⟨ProcessorV2 C⟩): #?serializer_info16 return "Serializer: Provides serialization"def get_info(self): #?validator_info
pass 1 of 223def get_info(self⟨ProcessorV2 C⟩): #?validator_info24 return "Validator: Provides validation"info_parts ← ['Logger: Provides logging capability', 'Serializer: Provides serialization', 'Validator: Provides validation']
46# Combine all parent info47info_parts→ ['Logger: Provides logging capability', 'Serializer: Provides serialization', 'Validator: Provides validation'] = [48 Logger<class '__main__.Logger'>.get_info(self),49 Serializer<class '__main__.Serializer'>.get_info(self),50 Validator<class '__main__.Validator'>.get_info(self),51]52return " | ".join(info_parts['Logger: Provides logging capability', 'Serializer: Provides serialization', 'Validator: Provides validation'])v3 ← ⟨ProcessorV3 D⟩
109v2 = ProcessorV2() #?create_v2110print(f"v2.get_info() = '{v2⟨ProcessorV2 C⟩.get_info()}'") #?v2_result111print(" → Override calls all three parent methods")112113# Solution 3: super() chain #?demo_v3114print("\n--- Solution 3: super() Chain ---")115v3→ ⟨ProcessorV3 D⟩ = ProcessorV3() #?create_v3116print(f"MRO: {[c.__name__(empty) for c in ProcessorV3.__mro__(<class '__main__.ProcessorV3'>, <class '__main__.LoggerChain'>, <class '__main__.SerializerChain'>, <class '__main__.ValidatorChain'>, <class '__main__.Base'>, <class 'object'>)]}") #?v3_mro117print(f"v3.get_info() = '{v3⟨ProcessorV3 D⟩.get_info()}'") #?v3_result118print(" → Each class calls super().get_info() to chain")outputv2.get_info() = 'Logger: Provides logging capability | Serializer: Provides serialization | Validator: Provides validation' → Override calls all three parent methods --- Solution 3: super() Chain --- MRO: ['ProcessorV3', 'LoggerChain', 'SerializerChain', 'ValidatorChain', 'Base', 'object']info ← Logger: Provides logging
56class LoggerChain: #?logger_chain57 def get_info(self⟨ProcessorV3 D⟩): #?chain_logger_info58 info→ Logger: Provides logging = "Logger: Provides logging"59 parent_info = super().get_info() if hasattr(super(), 'get_info') else ""60 return info + (" | " + parent_info if parent_info else "")info ← Serializer: Provides serialization
63class SerializerChain: #?serializer_chain64 def get_info(self⟨ProcessorV3 D⟩): #?chain_serializer_info65 info→ Serializer: Provides serialization = "Serializer: Provides serialization"66 parent_info = super().get_info() if hasattr(super(), 'get_info') else ""67 return info + (" | " + parent_info if parent_info else "")info ← Validator: Provides validation
70class ValidatorChain: #?validator_chain71 def get_info(self⟨ProcessorV3 D⟩): #?chain_validator_info72 info→ Validator: Provides validation = "Validator: Provides validation"73 parent_info = super().get_info() if hasattr(super(), 'get_info') else ""74 return info + (" | " + parent_info if parent_info else "")def get_info(self): #?chain_base_info
77class Base: #?chain_base78 def get_info(self⟨ProcessorV3 D⟩): #?chain_base_info79 return "" # End of chainparent_info ← (empty)
72info = "Validator: Provides validation"73parent_info→ (empty) = super().get_info() if hasattr(super(), 'get_info') else ""74return infoValidator: Provides validation + (" | " + parent_info(empty) if parent_info else "")parent_info ← Validator: Provides validation
65info = "Serializer: Provides serialization"66parent_info→ Validator: Provides validation = super().get_info() if hasattr(super(), 'get_info') else ""67return infoSerializer: Provides serialization + (" | " + parent_infoValidator: Provides validation if parent_info else "")parent_info ← Serializer: Provides serialization | Validator: Provides validation
58info = "Logger: Provides logging"59parent_info→ Serializer: Provides serialization | Validator: Provides validation = super().get_info() if hasattr(super(), 'get_info') else ""60return infoLogger: Provides logging + (" | " + parent_infoSerializer: Provides serialization | Validator: Provides validation if parent_info else "")print(f"v3.get_info() = '{v3.get_info()}'") #?v3_result
116print(f"MRO: {[c.__name__ for c in ProcessorV3.__mro__]}") #?v3_mro117print(f"v3.get_info() = '{v3⟨ProcessorV3 D⟩.get_info()}'") #?v3_result118print(" → Each class calls super().get_info() to chain")119120# Accessing specific parent methods #?access_specific121print("\n--- Accessing Specific Parent Methods ---")122print(f"Logger.get_info(proc) = '{Logger<class '__main__.Logger'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_logger123print(f"Serializer.get_info(proc) = '{Serializer.get_info(proc)}'") #?call_serializeroutputv3.get_info() = 'Logger: Provides logging | Serializer: Provides serialization | Validator: Provides validation' → Each class calls super().get_info() to chain --- Accessing Specific Parent Methods ---print(f"Logger.get_info(proc) = '{Logger.get_info(proc)}'") #?call_log…
121print("\n--- Accessing Specific Parent Methods ---")122print(f"Logger.get_info(proc) = '{Logger<class '__main__.Logger'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_logger123print(f"Serializer.get_info(proc) = '{Serializer<class '__main__.Serializer'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_serializer124print(f"Validator.get_info(proc) = '{Validator.get_info(proc)}'") #?call_validatoroutputLogger.get_info(proc) = 'Logger: Provides logging capability'def get_info(self): #?serializer_info
pass 2 of 215def get_info(self⟨DataProcessor A⟩): #?serializer_info16 return "Serializer: Provides serialization"print(f"Serializer.get_info(proc) = '{Serializer.get_info(proc)}'") #?…
122print(f"Logger.get_info(proc) = '{Logger.get_info(proc)}'") #?call_logger123print(f"Serializer.get_info(proc) = '{Serializer<class '__main__.Serializer'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_serializer124print(f"Validator.get_info(proc) = '{Validator<class '__main__.Validator'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_validator125print(" → Can always call parent method directly with instance")outputSerializer.get_info(proc) = 'Serializer: Provides serialization'def get_info(self): #?validator_info
pass 2 of 223def get_info(self⟨DataProcessor A⟩): #?validator_info24 return "Validator: Provides validation"print(f"Validator.get_info(proc) = '{Validator.get_info(proc)}'") #?ca…
123 print(f"Serializer.get_info(proc) = '{Serializer.get_info(proc)}'") #?call_serializer124 print(f"Validator.get_info(proc) = '{Validator<class '__main__.Validator'>.get_info(proc⟨DataProcessor A⟩)}'") #?call_validator125 print(" → Can always call parent method directly with instance")126127 # Best practices #?best_practices128 print("\n=== Best Practices for Conflicts ===")129 print("""130 1. Avoid conflicts by using unique method names131 2. If conflict unavoidable, override in child132 3. Use ParentClass.method(self) for explicit calls133 4. Design parent classes to work with super() chain134 5. Document which parent's method is used135 6. Consider composition over inheritance136 """)137138main()outputValidator.get_info(proc) = 'Validator: Provides validation' → Can always call parent method directly with instance === Best Practices for Conflicts === 1. Avoid conflicts by using unique method names 2. If conflict unavoidable, override in child 3. Use ParentClass.method(self) for explicit calls 4. Design parent classes to work with super() chain 5. Document which parent's method is used 6. Consider composition over inheritance
Leftmost parent wins. Explicit override if you need different behavior.
Exercise: practical.py
Build a game character with multiple capability classes