For the case of the most basic multiple inheritance:
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, b):
self.b = b
class C(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
I do not see why super()
should be used. I suppose you could implement it with kwargs, but that is surely less readable than the above method. I am yet to find any answers on stack overflow which are in favour of this method, yet surely for this case it is the most satisfactory?
There are a lot of questions marked as duplicate on this topic, but no satisfactory answers for this exact case. This question addresses multiple inheritance and the use of super()
for a diamond inheritance. In this case there is no diamond inheritance and neither parent class have any knowledge of each other, so they shouldn't need to call super()
like this suggests.
This answer deals with the use of super
in this scenario but without passing arguments to __init__
like is done here, and this answer deals with passing arguments but is again a diamond inheritance.
One correct way to use super
here would be
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 C1(A, B):
pass
class C2(A, B):
def __init__(self, a, b, **kwargs):
super().__init__(a=a, b=b, **kwargs)
c1 = C1(a="foo", b="bar")
c2 = C2(a="foo", b="bar")
The method resolution order for C
is [C, A, B, object]
. Each time super()
is called, it returns a proxy for the next class in the MRO, based on where super()
is called at the time.
You have two options when defining C
, depending on whether you want to define C.__init__
with a signature that mentions the two arguments A
and B
required for initialization. With C1
, C1.__init__
is not defined so A.__init__
will be called instead. With C2
, you need to explicitly call the next __init__
method in the chain.
C
, knowing that it is a subclass of A
and B
, has to at least provide the expected arguments for the known upstream __init__
methods.
A.__init__
will pass on everything except a
to the next class's __init__
method.
B.__init__
will pass on everything it receives except b
.
object.__init__
will finally be called, and assuming all previous classes correctly removed the keyword arguments they introduced, will receive no additional arguments.
Changing the order in which the various __init__
s are called means changing the MRO, which means altering the order of the base classes. If you want more control than that, then cooperative multiple inheritance is not for you.