python多继承使用super将构造函数传递给参数

时间:2016-01-19 18:42:54

标签: python multiple-inheritance super diamond-problem

考虑下面的python代码片段

class A(object):
    def __init__(self, a):
        self.a = a

class B(A):
    def __init__(self, a, b):
        super(B, self).__init__(a)
        self.b = b

class C(A):
    def __init__(self, a, c):
        super(C, self).__init__(a)
        self.c = c

class D(B, C):
    def __init__(self, a, b, c, d):
        #super(D,self).__init__(a, b, c) ???
        self.d = d

我想知道如何将abc传递给相应的基类构造函数。

谢谢,

3 个答案:

答案 0 :(得分:39)

好吧,在处理一般的多重继承时,你的基类(不幸的是)应该设计用于多继承。您的示例中的类BC不是,因此您无法找到在super中应用D的正确方法。

为多重继承设计基类的一种常用方法是让中级基类接受他们不想使用的__init__方法中的额外args,并将它们传递给它们致super电话。

这是在python中实现它的一种方法:

class A(object):
    def __init__(self,a):
        self.a=a

class B(A):
    def __init__(self,b,**kw):
        self.b=b
        super(B,self).__init__(**kw)

 class C(A):
    def __init__(self,c,**kw):
        self.c=c
        super(C,self).__init__(**kw)

class D(B,C):
    def __init__(self,a,b,c,d):
        super(D,self).__init__(a=a,b=b,c=c)
        self.d=d

这可以被视为令人失望,但这就是它的方式。

答案 1 :(得分:8)

不幸的是,如果不更改Base类,就无法使用super()来完成这项工作。对BC的构造函数的任何调用都将尝试调用Method Resolution Order中的下一个类,该类将始终为BC AB类构造函数所假设的C类。

另一种方法是显式调用构造函数,而不在每个类中使用super()

class A(object):
    def __init__(self, a):
        object.__init__()
        self.a = a

class B(A):
    def __init__(self, a, b):
        A.__init__(self, a)
        self.b = b

class C(object):
    def __init__(self, a, c):
        A.__init__(self, a)
        self.c = c

class D(B, C):
    def __init__(self, a, b, c, d):
        B.__init__(self, a, b)
        C.__init__(self, a, c)
        self.d = d 

这里仍有一个缺点,因为A构造函数将被调用两次,这在这个例子中并没有太大影响,但可能会导致更复杂的构造函数出现问题。您可以包含一个检查以防止构造函数多次运行。

class A(object):
    def __init__(self, a):
        if hasattr(self, 'a'):
            return
        # Normal constructor.

有人会称这是super()的缺点,这在某种意义上说,但它也只是一般多重继承的一个缺点。钻石继承模式通常容易出错。而且它们的许多变通方法会导致更加混乱且容易出错的代码。有时候,最好的答案是尝试重构代码以减少多重继承。

答案 2 :(得分:0)

我对这里的答案并不完全满意,因为有时使用不同的参数为每个基类分别调用 super() 而不对其进行重组会非常方便。因此,我创建了一个名为 multinherit 的包,您可以使用该包轻松解决此问题。 https://github.com/DovaX/multinherit

from multinherit.multinherit import multi_super

class A(object):
    def __init__(self, a):
        self.a = a
        print(self.a)


class B(A):
    def __init__(self, a, b):
        multi_super(A,self,a=a)
        self.b = b
        print(self.b)


class C(A):
    def __init__(self, a, c):
        multi_super(A,self,a=a)
        self.c = c
        print(self.c)


class D(B, C):
    def __init__(self, a, b, c, d):
        multi_super(B,self,a=a,b=b)
        multi_super(C,self,a=a,c=c)
        self.d = d
        print(self.d)
   

print()
print("d3")            
d3=D(1,2,3,4)
print(d3._classes_initialized)

>>> d3
>>> 1
>>> 2
>>> 3
>>> 4
>>> [<class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>]