Expert Python Programming(Third Edition)
上QQ阅读APP看书,第一时间看更新

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.