I have three classes: A
, B
and C
.
C
inherits from A
and B
(in this order). The constructor signatures of A
and B
are different. How can I call the __init__
methods of both parent classes?
My endeavour in code:
class A(object):
def __init__(self, a, b):
super(A, self).__init__()
print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))
class B(object):
def __init__(self, q):
super(B, self).__init__()
print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))
class C(A, B):
def __init__(self):
super(A, self).__init__(1, 2)
super(B, self).__init__(3)
c = C()
yields the error:
Traceback (most recent call last):
File "test.py", line 16, in <module>
c = C()
File "test.py", line 13, in __init__
super(A, self).__init__(1, 2)
TypeError: __init__() takes 2 positional arguments but 3 were given
I found this resource which explains mutiple inheritance with different set of arguments, but they suggest to use *args
and **kwargs
to use for all argument. I consider this very ugly, since I cannot see from the constructor call in the child class what kind of parameters I pass to the parent classes.
Do not use super(baseclass, ...)
unless you know what you are doing. The first argument to super()
tells it what class to skip when looking for the next method to use. E.g. super(A, ...)
will look at the MRO, find A
, then start looking for __init__
on the next baseclass, not A
itself. For C
, the MRO is (C, A, B, object)
, so super(A, self).__init__
will find B.__init__
.
For these cases, you don't want to use cooperative inheritance but directly reference A.__init__
and B.__init__
instead. super()
should only be used if the methods you are calling have the same signature or will swallow unsupported arguments with *args
and **vargs
. In that case just the one super(C, self).__init__()
call would be needed and the next class in the MRO order would take care of chaining on the call.
Putting it differently: when you use super()
, you can not know what class will be next in the MRO, so that class better support the arguments you pass to it. If that isn't the case, do not use super()
.
Calling the base __init__
methods directly:
class A(object):
def __init__(self, a, b):
print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))
class B(object):
def __init__(self, q):
print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))
class C(A, B):
def __init__(self):
# Unbound functions, so pass in self explicitly
A.__init__(self, 1, 2)
B.__init__(self, 3)
Using cooperative super()
:
class A(object):
def __init__(self, a=None, b=None, *args, **kwargs):
super().__init__(*args, **kwargs)
print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))
class B(object):
def __init__(self, q=None, *args, **kwargs):
super().__init__(*args, **kwargs)
print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))
class C(A, B):
def __init__(self):
super().__init__(a=1, b=2, q=3)