pythonmultiple-inheritancesuperdiamond-problem

python multiple inheritance passing arguments to constructors using super


Consider the following snippet of python code

class A(object):
    def __init__(self, a):
        self.a = a

class B(A):
    def __init__(self, a, b):
        super(B, self).__init__(a)
        self.b = b

class C(A):
    def __init__(self, a, c):
        super(C, self).__init__(a)
        self.c = c

class D(B, C):
    def __init__(self, a, b, c, d):
        #super(D,self).__init__(a, b, c) ???
        self.d = d

I am wondering how can I pass a, b and c to corresponding base classes' constructors.


Solution

  • Well, when dealing with multiple inheritance in general, your base classes (unfortunately) should be designed for multiple inheritance. Classes B and C in your example aren't, and thus you couldn't find a proper way to apply super in D.

    One of the common ways of designing your base classes for multiple inheritance, is for the middle-level base classes to accept extra args in their __init__ method, which they are not intending to use, and pass them along to their super call.

    Here's one way to do it in python:

    # Multiple inheritance in Python 3
    
    # Define class A with attribute a
    class A:
        def __init__(self, a):
            self.a = a
    
        def method_a(self):
            print(f"Method from A: a = {self.a}")
    
    
    # Define class B that inherits from A with additional attribute b
    class B(A):
        def __init__(self, b, **kwargs):
            super().__init__(**kwargs)  # Call A's __init__
            self.b = b
    
        def method_b(self):
            print(f"Method from B: b = {self.b}")
    
    
    # Define class C that inherits from A with additional attribute c
    class C(A):
        def __init__(self, c, **kwargs):
            super().__init__(**kwargs)  # Call A's __init__
            self.c = c
    
        def method_c(self):
            print(f"Method from C: c = {self.c}")
    
    
    # Define class D that inherits from both B and C with additional attribute d
    class D(B, C):
        def __init__(self, a, b, c, d):
            super().__init__(a=a, b=b, c=c)  # super() will follow the MRO
            self.d = d
    
        def method_d(self):
            print(f"Method from D: d = {self.d}")
    
    
    # Example usage
    if __name__ == "__main__":
        obj = D(a="Value from A", b="Value from B", c="Value from C", d="Value from D")
    
        # Calling methods from A, B, C, and D
        obj.method_a()  # From A
        obj.method_b()  # From B
        obj.method_c()  # From C
        obj.method_d()  # From D
    
        # Print the Method Resolution Order (MRO) for class D
        print("\nMRO of class D:")
        for cls in D.mro():
            print(cls)
    

    This can be viewed as disappointing, but that's just the way it is.