Python - 如何避免调用祖父构造函数(多重继承)



BC从类A继承。类 D 继承自 BC

class A:
    def __init__(self):
        print('A')
class B(A):
    def __init__(self):
        A.__init__(self)
        print('B')
class C(A):
    def __init__(self):
        A.__init__(self)
        print('C')
class D(B, C):
    def __init__(self):
        B.__init__(self)
        C.__init__(self)
        print('D')
d = D()

这将输出:

A
B
A
C
D

有没有办法避免对A构造函数的双重调用?

注意:在我的现实世界中,来自 BC__init__方法没有相同的签名,因此使用 super() 不是一种选择(据我了解......

干杯!

如果您知道A将在多个基类中使用,则可以让它自行检测双重初始化。

class A:
    def __init__(self):
        if hasattr(self, '_initialized'):
            return
        print('A')
        self._initialized = true

这绝对是应该使用super的情况。可以使用 *args**kwargs 来控制参数在每个级别调度的方式。请务必注意,类声明 of D 中基类的顺序控制着超级搜索类查找下一个方法的顺序。因此,它还将控制使用位置参数的顺序。

例如。

class A:
    def __init__(self, a, *, caller):
        print("A.__init__(a={!r}) called by {}".format(a, caller))
class B(A):
    def __init__(self, b, *args, caller, **kwargs):
        super().__init__(caller="B", *args, **kwargs )
        print("B.__init__(b={!r}) called by {}".format(b, caller))
class C(A):
    def __init__(self, c, *args, caller, **kwargs):
        super().__init__(caller="C", *args, **kwargs)
        print("C.__init__(c={!r}) called by {}".format(c, caller))
class D(B, C):
    def __init__(self, d, *args, **kwargs):
        super().__init__(caller="D", *args, **kwargs)
        print("D.__init__(d={!r}) called by {}".format(d, "user"))
try:
    print("too many arguments")
    D(a=1, b=2, c=3, d=4, e=5)
except TypeError as e:
    print(e)
try:
    print("too few arguments")
    D(a=1, c=3, d=4)
except TypeError as e:
    print(e)
print("using keyword args")
D(a=1, b=2, c=3, d=4)
print("using positional args")
D(4, 2, 3, 1) # as B comes before C, B's argument must come before C's
print("using a mix of positional and keyword args")
D(4, 2, a=1, c=3)

其输出为:

too many arguments
__init__() got an unexpected keyword argument 'e'
too few arguments
__init__() missing 1 required positional argument: 'b'
using keyword args
A.__init__(a=1) called by C
C.__init__(c=3) called by B
B.__init__(b=2) called by D
D.__init__(d=4) called by user
using positional args
A.__init__(a=1) called by C
C.__init__(c=3) called by B
B.__init__(b=2) called by D
D.__init__(d=4) called by user
using a mix of positional and keyword args
A.__init__(a=1) called by C
C.__init__(c=3) called by B
B.__init__(b=2) called by D
D.__init__(d=4) called by user

如果不确定搜索基类的顺序,请使用 D.mro()mro 代表 方法解析顺序。它返回将搜索属性(在我们的例子中为 __init__)的类列表。

>>> print(D.mro())
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, 
        <class 'object'>]