Search code examples
pythonoopmultiple-inheritance

Multiple Inheritance with kwargs


Problem

I came across this code in Object Oriented Programming by Dusty Phillips (simplified for brevity) and I unsure about a specific part of this definition.

class A:
    def __init__(self, a, **kwargs):
        super().__init__(**kwargs)
        self.a = a

class B:
    def __init__(self, b, **kwargs):
        super().__init__(**kwargs)
        self.b = b

class C(A, B):
    def __init__(self, c, **kwargs):
        super().__init__(**kwargs)
        self.c = c

Questions

  1. Since the method resolution order is (__main__.C, __main__.A, __main__.B, object), could class B be defined in the following way instead?
class B:
    def __init__(self, b):
        self.b = b
  1. Isn't super().__init__(**kwargs) in class B redundant, since any surplus kwargs passed to C will be passed to object, raising?

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

  1. Is this a safeguard for if C was defined as class C(B, A) instead of class C(A, B)?

Solution

  • Since the method resolution order is (__main__.C, __main__.A, __main__.B, object), could class B be defined in the following way instead?

    No, because then this would fail:

    class D:
        def __init__(self, d, **kwargs):
            self.d = d
            super().__init__(**kwargs)
    
    class E(C, D):
        def __init__(self, e, **kwargs):
            self.e = e
            super().__init__(**kwargs)
    

    The MRO of E is (E, C, A, B, D, object), so B must call super().__init__ otherwise D.__init__ won't be called.

    Isn't super().__init__(**kwargs) in class B redundant, since any surplus kwargs passed to C will be passed to object, raising?

    No, because the surplus kwargs will go to D.__init__ in the above example. But even without that, it is not redundant to raise an error when you call a constructor with too many arguments; it is desirable to have an error message informing you about your incorrect code, rather than for the mistake to go undetected.

    Is this a safeguard for if C was defined as class C(B, A) instead of class C(A, B)?

    In some sense, sure; but really it's a safeguard for B occurring in any class hierarchy, so long as the other classes in the hierarchy follow the same rule of calling super().__init__(**kwargs).