Heterogeneous arguments
Another issue with super usage occurs if methods of classes within the class hierarchy use inconsistent argument sets. How can a class call its base class an __init__() code if it doesn't have the same signature? This leads to the following problem:
class CommonBase: def __init__(self): print('CommonBase') super().__init__() class Base1(CommonBase): def __init__(self): print('Base1') super().__init__() class Base2(CommonBase): def __init__(self, arg): print('base2') super().__init__() class MyClass(Base1 , Base2): def __init__(self, arg): print('my base') super().__init__(arg)
An attempt to create a MyClass instance will raise TypeError due to a mismatch of the parent classes' __init__() signatures:
>>> MyClass(10) my base Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in __init__ TypeError: __init__() takes 1 positional argument but 2 were given
One solution would be to use arguments and keyword arguments packing with *args and **kwargs magic so that all constructors pass along all the parameters, even if they do not use them:
class CommonBase: def __init__(self, *args, **kwargs): print('CommonBase') super().__init__() class Base1(CommonBase): def __init__(self, *args, **kwargs): print('Base1') super().__init__(*args, **kwargs) class Base2(CommonBase): def __init__(self, *args, **kwargs): print('base2') super().__init__(*args, **kwargs) class MyClass(Base1 , Base2): def __init__(self, arg): print('my base') super().__init__(arg)
With this approach, the parent class signatures will always match:
>>> _ = MyClass(10) my base Base1 base2 CommonBase
This is an awful fix though, because it makes all constructors accept any kind of parameters. It leads to weak code, since anything can be passed and gone through. Another solution is to use the explicit __init__() calls of specific classes in MyClass, but this would lead to the first pitfall.
In the next section, we will discuss the best practices.