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.py
Replay: real traced execution (multi-file project)
# 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()














































  1. """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()
  2. 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_describe
    output=== Basic Multiple Inheritance ===
    --- Single Inheritance (Fish) ---
  3. def swim(self): #?swim_method

    pass 1 of 3
    6def swim(self⟨Fish A⟩): #?swim_method7    return f"{self.__class__.__name__Fish} is swimming"
    All 3 passes — pass 1 is the card above
    passselfself.__class__.__name__
    1⟨Fish A⟩Fish
    2⟨Duck B⟩Duck
    3⟨Penguin C⟩Penguin
  4. print(fish.swim()) #?fish_swim

    55fish = Fish() #?create_fish56print(fish⟨Fish A⟩.swim()) #?fish_swim57print(f"describe(): {fish⟨Fish A⟩.describe()}") #?fish_describe
    outputFish is swimming
  5. def describe(self): #?swimmer_describe

    pass 1 of 3
    9def describe(self⟨Fish A⟩): #?swimmer_describe10    return "I can swim"
    All 3 passes — pass 1 is the card above
    passself
    1⟨Fish A⟩
    2⟨Duck B⟩
    3⟨Penguin C⟩
  6. 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_fly
    outputdescribe(): I can swim
    
    --- Multiple Inheritance (Duck) ---
  7. print(duck.swim()) #?duck_swim

    61duck = Duck() #?create_duck62print(duck⟨Duck B⟩.swim()) #?duck_swim63print(duck⟨Duck B⟩.fly()) #?duck_fly64print(duck.walk()) #?duck_walk
    outputDuck is swimming
  8. def fly(self): #?fly_method

    16def fly(self⟨Duck B⟩): #?fly_method17    return f"{self.__class__.__name__Duck} is flying"
  9. 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_result
    outputDuck is flying
  10. def walk(self): #?walk_method

    pass 1 of 2
    26def walk(self⟨Duck B⟩): #?walk_method27    return f"{self.__class__.__name__Duck} is walking"
  11. print(duck.walk()) #?duck_walk

    63print(duck.fly()) #?duck_fly64print(duck⟨Duck B⟩.walk()) #?duck_walk65print(f"describe(): {duck⟨Duck B⟩.describe()}") #?duck_describe_result
    outputDuck is walking
  12. penguin ← ⟨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_walk
    outputdescribe(): I can swim
    
    --- Multiple Inheritance (Penguin) ---
  13. 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_result
    outputPenguin is swimming
  14. def walk(self): #?walk_method

    pass 2 of 2
    26def walk(self⟨Penguin C⟩): #?walk_method27    return f"{self.__class__.__name__Penguin} is walking"
  15. print(penguin.walk()) #?penguin_walk

    70print(penguin.swim()) #?penguin_swim71print(penguin⟨Penguin C⟩.walk()) #?penguin_walk72print(f"describe(): {penguin⟨Penguin C⟩.describe()}") #?penguin_describe_result
    outputPenguin is walking
  16. print(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 wins
        
  17. main()

    101main()

class Child(Parent1, Parent2): inherits from both. Child has all methods.

multiple inheritance Inherit from multiple classes: `class C(A, B):`. Gets methods from both.

Method Resolution Order

How Python decides which method to call.

mro.py
Replay: real traced execution (multi-file project)
# 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()





































  1. 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()
  2. 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_mro
    output=== 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:
  3. for i, cls in enumerate(D.__mro__): #?loop_mro

    pass 1 of 5
    48print("\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_item
    output  1. D
    All 5 passes — pass 1 is the card above
    passicls.__name__
    10D
    21B
    32C
    43A
    54object
  4. 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 ---
  5. def method(self): #?b_method

    11class B(A): #?class_b12    def method(self⟨D A⟩): #?b_method13        return "B.method()"
  6. 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)
  7. def who_am_i(self): #?c_who

    pass 1 of 2
    20def who_am_i(self⟨D A⟩): #?c_who21    return "I am C"
  8. 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'>)
  9. def method(self): #?c_method

    16class C(A): #?class_c17    def method(self⟨E B⟩): #?c_method18        return "C.method()"
  10. 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)
  11. def who_am_i(self): #?c_who

    pass 2 of 2
    20def who_am_i(self⟨E B⟩): #?c_who21    return "I am C"
  12. 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 algorithm
        
  13. main()

    108main()

ClassName.__mro__ shows lookup order. Follows C3 linearization algorithm.

MRO Method Resolution Order. Determines which parent's method is used.

The diamond problem

When two parents share a grandparent.

diamond_problem.py
Replay: real traced execution (multi-file project)
# 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()

































  1. main()

    125main()
  2. 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():
    ------------------------------
  3. 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 chain
    outputD.__init__() called
  4. def __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 MRO
    outputB.__init__() called
  5. def __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 MRO
    outputC.__init__() called
  6. self.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__() called
  7. self.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"
  8. 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 ---
  9. def greet(self): #?b_greet

    28def greet(self⟨D A⟩): #?b_greet29    return "Hello from B"
  10. 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'
  11. def identify(self): #?a_identify

    18def identify(self⟨D A⟩): #?a_identify19    return f"A (value={self.valuefrom A})"
  12. 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"
        
  13. main()

    125main()

A → B, C → D. Python's MRO ensures each class is called once.

diamond problem Ambiguity when same method inherited through multiple paths. MRO resolves it.

super() chain

Cooperative multiple inheritance.

obj
super_chain.py
Replay: real traced execution (multi-file project)
# 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()



































  1. main()

    127main()
  2. 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 ---
  3. def __init__(self, **kwargs): #?combined_init

    pass 1 of 2
    49class 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 kwargs
    outputCombined.__init__(kwargs={'a_param': 'A!', 'b_param': 'B!', 'c_param': 'C!'})
  4. def __init__(self, a_param="default_a", **kwargs): #?feature_a_init

    pass 1 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=A!, kwargs={'b_param': 'B!', 'c_param': 'C!'})
  5. def __init__(self, b_param="default_b", **kwargs): #?feature_b_init

    pass 1 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=B!, kwargs={'c_param': 'C!'})
  6. def __init__(self, c_param="default_c", **kwargs): #?feature_c_init

    pass 1 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=C!, kwargs={})
  7. def __init__(self, **kwargs): #?base_init

    pass 1 of 2
    3class Base: #?base_class4    def __init__(self⟨Combined A⟩, **kwargs): #?base_init5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  8. 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!
  9. 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_result
    output
    Attributes:
      obj.a_param = 'A!'
      obj.b_param = 'B!'
      obj.c_param = 'C!'
    
    --- Calling process() ---
  10. def process(self): #?combined_process

    54def process(self⟨Combined A⟩): #?combined_process55    print("Combined.process()")56    result = super().process()  # Start the chain57    return ["Combined"] + result
    outputCombined.process()
  11. 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"] + result
    outputFeatureA.process()
  12. 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"] + result
    outputFeatureB.process()
  13. 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"] + result
    outputFeatureC.process()
  14. def process(self): #?base_process

    8def process(self⟨Combined A⟩): #?base_process9    print("Base.process()")10    return ["Base"]
    outputBase.process()
  15. result ← ['Base']

    44print("FeatureC.process()")45result→ ['Base'] = super().process()  # Call next in chain46return ["FeatureC"] + result['Base']
  16. result ← ['FeatureC', 'Base']

    32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process()  # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']
  17. result ← ['FeatureB', 'FeatureC', 'Base']

    20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process()  # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']
  18. result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']

    55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process()  # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']
  19. 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_instance
    output
    Result: ['Combined', 'FeatureA', 'FeatureB', 'FeatureC', 'Base']
    
    ==================================================
    --- Creating Combined with defaults ---
  20. def __init__(self, **kwargs): #?combined_init

    pass 2 of 2
    49class 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 kwargs
    outputCombined.__init__(kwargs={})
  21. def __init__(self, a_param="default_a", **kwargs): #?feature_a_init

    pass 2 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=default_a, kwargs={})
  22. def __init__(self, b_param="default_b", **kwargs): #?feature_b_init

    pass 2 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=default_b, kwargs={})
  23. def __init__(self, c_param="default_c", **kwargs): #?feature_c_init

    pass 2 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=default_c, kwargs={})
  24. def __init__(self, **kwargs): #?base_init

    pass 2 of 2
    3class Base: #?base_class4    def __init__(self⟨Combined B⟩, **kwargs): #?base_init5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  25. 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_c
  26. obj2 ← ⟨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.
        
  1. main()

    126main()
  2. 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 ---
  3. def __init__(self, **kwargs):

    pass 1 of 2
    49class 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 kwargs
    outputCombined.__init__(kwargs={'a_param': 'alpha', 'b_param': 'beta', 'c_param': 'gamma'})
  4. def __init__(self, a_param="default_a", **kwargs):

    pass 1 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=alpha, kwargs={'b_param': 'beta', 'c_param': 'gamma'})
  5. def __init__(self, b_param="default_b", **kwargs):

    pass 1 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=beta, kwargs={'c_param': 'gamma'})
  6. def __init__(self, c_param="default_c", **kwargs):

    pass 1 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=gamma, kwargs={})
  7. def __init__(self, **kwargs):

    pass 1 of 2
    3class Base:4    def __init__(self⟨Combined A⟩, **kwargs):5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  8. 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_paramgamma
  9. obj ← ⟨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() ---
  10. def process(self):

    54def process(self⟨Combined A⟩):55    print("Combined.process()")56    result = super().process()  # Start the chain57    return ["Combined"] + result
    outputCombined.process()
  11. def process(self):

    19def process(self⟨Combined A⟩):20    print("FeatureA.process()")21    result = super().process()  # Call next in chain22    return ["FeatureA"] + result
    outputFeatureA.process()
  12. def process(self):

    31def process(self⟨Combined A⟩):32    print("FeatureB.process()")33    result = super().process()  # Call next in chain34    return ["FeatureB"] + result
    outputFeatureB.process()
  13. def process(self):

    43def process(self⟨Combined A⟩):44    print("FeatureC.process()")45    result = super().process()  # Call next in chain46    return ["FeatureC"] + result
    outputFeatureC.process()
  14. def process(self):

    8def process(self⟨Combined A⟩):9    print("Base.process()")10    return ["Base"]
    outputBase.process()
  15. result ← ['Base']

    44print("FeatureC.process()")45result→ ['Base'] = super().process()  # Call next in chain46return ["FeatureC"] + result['Base']
  16. result ← ['FeatureC', 'Base']

    32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process()  # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']
  17. result ← ['FeatureB', 'FeatureC', 'Base']

    20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process()  # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']
  18. result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']

    55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process()  # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']
  19. 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 ---
  20. def __init__(self, **kwargs):

    pass 2 of 2
    49class Combined(FeatureA, FeatureB, FeatureC):50    def __init__(self⟨Combined B⟩, **kwargs):51        print(f"Combined.__init__(kwargs={kwargs{}})")52        super().__init__(**kwargs)  # Pass all kwargs
    outputCombined.__init__(kwargs={})
  21. def __init__(self, a_param="default_a", **kwargs):

    pass 2 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=default_a, kwargs={})
  22. def __init__(self, b_param="default_b", **kwargs):

    pass 2 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=default_b, kwargs={})
  23. def __init__(self, c_param="default_c", **kwargs):

    pass 2 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=default_c, kwargs={})
  24. def __init__(self, **kwargs):

    pass 2 of 2
    3class Base:4    def __init__(self⟨Combined B⟩, **kwargs):5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  25. 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_c
  26. obj2 ← ⟨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.
        
  1. main()

    126main()
  2. 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 ---
  3. def __init__(self, **kwargs):

    pass 1 of 2
    49class 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 kwargs
    outputCombined.__init__(kwargs={'a_param': 'left', 'b_param': 'middle', 'c_param': 'right'})
  4. def __init__(self, a_param="default_a", **kwargs):

    pass 1 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=left, kwargs={'b_param': 'middle', 'c_param': 'right'})
  5. def __init__(self, b_param="default_b", **kwargs):

    pass 1 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=middle, kwargs={'c_param': 'right'})
  6. def __init__(self, c_param="default_c", **kwargs):

    pass 1 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=right, kwargs={})
  7. def __init__(self, **kwargs):

    pass 1 of 2
    3class Base:4    def __init__(self⟨Combined A⟩, **kwargs):5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  8. 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_paramright
  9. obj ← ⟨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() ---
  10. def process(self):

    54def process(self⟨Combined A⟩):55    print("Combined.process()")56    result = super().process()  # Start the chain57    return ["Combined"] + result
    outputCombined.process()
  11. def process(self):

    19def process(self⟨Combined A⟩):20    print("FeatureA.process()")21    result = super().process()  # Call next in chain22    return ["FeatureA"] + result
    outputFeatureA.process()
  12. def process(self):

    31def process(self⟨Combined A⟩):32    print("FeatureB.process()")33    result = super().process()  # Call next in chain34    return ["FeatureB"] + result
    outputFeatureB.process()
  13. def process(self):

    43def process(self⟨Combined A⟩):44    print("FeatureC.process()")45    result = super().process()  # Call next in chain46    return ["FeatureC"] + result
    outputFeatureC.process()
  14. def process(self):

    8def process(self⟨Combined A⟩):9    print("Base.process()")10    return ["Base"]
    outputBase.process()
  15. result ← ['Base']

    44print("FeatureC.process()")45result→ ['Base'] = super().process()  # Call next in chain46return ["FeatureC"] + result['Base']
  16. result ← ['FeatureC', 'Base']

    32print("FeatureB.process()")33result→ ['FeatureC', 'Base'] = super().process()  # Call next in chain34return ["FeatureB"] + result['FeatureC', 'Base']
  17. result ← ['FeatureB', 'FeatureC', 'Base']

    20print("FeatureA.process()")21result→ ['FeatureB', 'FeatureC', 'Base'] = super().process()  # Call next in chain22return ["FeatureA"] + result['FeatureB', 'FeatureC', 'Base']
  18. result ← ['FeatureA', 'FeatureB', 'FeatureC', 'Base']

    55print("Combined.process()")56result→ ['FeatureA', 'FeatureB', 'FeatureC', 'Base'] = super().process()  # Start the chain57return ["Combined"] + result['FeatureA', 'FeatureB', 'FeatureC', 'Base']
  19. 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 ---
  20. def __init__(self, **kwargs):

    pass 2 of 2
    49class Combined(FeatureA, FeatureB, FeatureC):50    def __init__(self⟨Combined B⟩, **kwargs):51        print(f"Combined.__init__(kwargs={kwargs{}})")52        super().__init__(**kwargs)  # Pass all kwargs
    outputCombined.__init__(kwargs={})
  21. def __init__(self, a_param="default_a", **kwargs):

    pass 2 of 2
    13class 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 up
    outputFeatureA.__init__(a_param=default_a, kwargs={})
  22. def __init__(self, b_param="default_b", **kwargs):

    pass 2 of 2
    25class 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 up
    outputFeatureB.__init__(b_param=default_b, kwargs={})
  23. def __init__(self, c_param="default_c", **kwargs):

    pass 2 of 2
    37class 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 up
    outputFeatureC.__init__(c_param=default_c, kwargs={})
  24. def __init__(self, **kwargs):

    pass 2 of 2
    3class Base:4    def __init__(self⟨Combined B⟩, **kwargs):5        print(f"Base.__init__(kwargs={kwargs{}})")6        # End of chain - absorb remaining kwargs
    outputBase.__init__(kwargs={})
  25. 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_c
  26. obj2 ← ⟨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.

conflict_resolution.py
Replay: real traced execution (multi-file project)
# 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()






















































  1. 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()
  2. 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']
  3. def get_info(self): #?logger_info

    pass 1 of 4
    7def get_info(self⟨DataProcessor A⟩): #?logger_info8    return "Logger: Provides logging capability"
    All 4 passes — pass 1 is the card above
    passself
    1⟨DataProcessor A⟩
    2⟨ProcessorV1 B⟩
    3⟨ProcessorV2 C⟩
    4⟨DataProcessor A⟩
  4. 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 ---
  5. 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)
  6. 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 ---
  7. 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)
  8. def get_info(self): #?serializer_info

    pass 1 of 2
    15def get_info(self⟨ProcessorV2 C⟩): #?serializer_info16    return "Serializer: Provides serialization"
  9. def get_info(self): #?validator_info

    pass 1 of 2
    23def get_info(self⟨ProcessorV2 C⟩): #?validator_info24    return "Validator: Provides validation"
  10. 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'])
  11. 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']
  12. 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 "")
  13. 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 "")
  14. 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 "")
  15. def get_info(self): #?chain_base_info

    77class Base: #?chain_base78    def get_info(self⟨ProcessorV3 D⟩): #?chain_base_info79        return ""  # End of chain
  16. parent_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 "")
  17. 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 "")
  18. 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 "")
  19. 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_serializer
    outputv3.get_info() = 'Logger: Provides logging | Serializer: Provides serialization | Validator: Provides validation'
      → Each class calls super().get_info() to chain
    
    --- Accessing Specific Parent Methods ---
  20. 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_validator
    outputLogger.get_info(proc) = 'Logger: Provides logging capability'
  21. def get_info(self): #?serializer_info

    pass 2 of 2
    15def get_info(self⟨DataProcessor A⟩): #?serializer_info16    return "Serializer: Provides serialization"
  22. 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'
  23. def get_info(self): #?validator_info

    pass 2 of 2
    23def get_info(self⟨DataProcessor A⟩): #?validator_info24    return "Validator: Provides validation"
  24. 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