多重继承如何与 super() 和不同的 __init__() 参数一起工作?

Posted

技术标签:

【中文标题】多重继承如何与 super() 和不同的 __init__() 参数一起工作?【英文标题】:How does multiple inheritance work with the super() and different __init__() arguments? 【发布时间】:2013-05-27 03:29:12 【问题描述】:

我只是在深入研究一些更高级的 python 主题(嗯,至少对我来说是高级的)。我现在正在阅读有关多重继承以及如何使用 super() 的内容。我或多或少了解 super 函数的使用方式,但是 (1) 这样做有什么问题?:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        First.__init__(self)
        Second.__init__(self)
        print "that's it"

关于 super(),Andrew Kuchlings paper on Python Warts 说:

当 Derived 类继承时 super() 的使用也是正确的 来自多个基类,其中部分或全部具有 init 方法

所以我把上面的例子改写如下:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__(self)
        print "that's it"

然而,这只会运行它可以找到的第一个 init,它位于 First 中。 (2) 可以使用super() 运行来自FirstSecond 的init,如果可以,如何运行? 运行super(Third, self).__init__(self) 两次只是首先运行。init() 两次..

增加一些混乱。如果继承类的 init() 函数采用不同的参数会怎样。例如,如果我有这样的事情:

class First(object):
    def __init__(self, x):
        print "first"

class Second(object):
    def __init__(self, y, z):
        print "second"

class Third(First, Second):
    def __init__(self, x, y, z):
        First.__init__(self, x)
        Second.__init__(self, y, z)
        print "that's it"

(3) 如何使用 super() 为不同的继承类初始化函数提供相关参数?

欢迎所有提示!

ps。由于我有几个问题,我将它们加粗并编号..

【问题讨论】:

super(Class, self) 返回一个对象,因此您不应使用.__init__(self) 调用它,而应使用.__init__()。您可以在答案中看到这一点,但您原来的第一个代码返回异常 TypeError: __init__() takes exactly 1 argument (2 given) 【参考方案1】:

对于问题2,你需要在每个类中调用super:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print "first"

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

对于问题 3,这是无法做到的,您的方法需要具有相同的签名。但是你可以忽略父类中的一些参数或使用关键字参数。

【讨论】:

所以你的意思是super()的使用依赖于继承类中super()的使用?如果我不控制继承的类怎么办?然后我会像我的第一个示例那样简单地执行 First.__init__(self) 和 Second.__init__(self) 吗? 是的,“super”是一个协作进程,所有类都需要使用该机制,否则它就不起作用。你可以在这里找到更多信息:rhettinger.wordpress.com/2011/05/26/super-considered-super 是的,如果层次结构中的一个类不使用 super,我认为你必须使用“旧式”方式 :) 谢谢。最后一个问题;为什么 super() 仍然添加到语言中?它比“旧式”有什么优势? 我看到了两个优点:您不必显式引用父类,并且在多重继承的情况下您不必调用多个父构造函数......同样在 python3 中,您只有调用 super().something() 而不是 super(Class, self).something() 您说:“在多重继承的情况下,您不必调用多个父构造函数”。不过我不明白,因为如果我调用 super(CurrentClass, self).__init__() (就像我在第二段代码中所做的那样)它只运行第一个继承类的 init 函数而不是所有的 init继承的类。所以这意味着在进行多重继承时不能使用 super() .. 对吗?【参考方案2】:

1) 像你在 1 中所做的那样做没有错,如果你想使用基类的属性,那么你必须调用基类 init() 或者即使您使用基类中的方法,该方法使用其自己类的属性,然后您必须调用基类 init()

2) 你不能使用 super 来运行 First 和 Second 的 init,因为 python 使用 MRO(方法解析顺序)

查看以下代码,这是菱形层次结构

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

class B(A):
    def __init__(self):
        self.b = 'b'
        print self.b

class C(A):
    def __init__(self):
        self.c = 'c'
        print self.c

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

d = D()
print D.mro()

打印出来:

d
b
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>]

python 的 MRO 是 D,B,C,A

如果 B 没有 init 方法,它适用于 C。

3) 你不能这样做所有的方法都需要有相同的签名。

【讨论】:

以上是关于多重继承如何与 super() 和不同的 __init__() 参数一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

python super()函数的用法与多重继承

多重继承和调用 super()

具有多重继承的 Python super().__init__()

python中多重继承与获取对象

多重继承中无用的超级? [复制]

016: class and objects > 多重继承与多态的例子