问题 在Python中创建两次Object


我读过Expert Python Programming,它有一个多继承的例子。书的作者已经解释过,但我不明白,所以我想有另一种观点。

该示例显示了该对象 B 创建了两次!

你能给我一个直观的解释吗?

In [1]: class A(object):
    ...:     def __init__(self):
    ...:         print "A"
    ...:         super(A, self).__init__()

In [2]: class B(object):
    ...:     def __init__(self):
    ...:         print "B"
    ...:         super(B, self).__init__()

In [3]: class C(A,B):
    ...:     def __init__(self):
    ...:         print "C"
    ...:         A.__init__(self)
    ...:         B.__init__(self)

In [4]: print "MRO:", [x.__name__ for x in C.__mro__]
MRO: ['C', 'A', 'B', 'object']

In [5]: C()
C
A
B
B
Out[5]: <__main__.C at 0x3efceb8>

书的作者说:

这是因为 A.__init__(self) 打电话,这是用   C实例,从而制作 super(A, self).__init__() 呼叫 B的构造函数

我没有得到它的想法是如何 A.__init__(self) 打电话会 super(A, self).__init__() 呼叫 B的构造函数


12655
2018-02-25 12:10


起源

你为什么不用 super 在C的init方法? - Tom Dalton
如果你想到的话 super 调用含义'调用MRO中的下一个方法',而不是“调用我的父类的方法”,那么这种行为应该更有意义。在这种情况下,你看到B打印两次的原因是因为super已经安排调用B的init(由A的init),所以当你从C中明确地调用B.init时,你会得到第二次调用。 - Tom Dalton
请参阅Raymond Hettinger关于超级的演讲,可能有助于更有意义: youtube.com/watch?v=EiOglTERPEo - Tom Dalton
关于您的问题标题,该对象不是“创建两次”。该 __init__ 函数被调用两次,但在此之前已经创建了对象。 - interjay
澄清@interjay的评论,__ init __()方法是一个 初始化不是 构造函数。 C实例由__new __()创建,然后传递给C .__ init __()。对于已经创建的唯一对象,C的__init __()中的错误代码导致对B的__init __()的两次单独调用。 - Peter Hansen


答案:


super() 只是意味着“下一行”,其中的行是 MRO  ['C', 'A', 'B', 'object']。所以接下来就行了 A 是 B

mro根据称为的算法计算 C3线性化。 当你使用 super(),Python就是这个顺序。当你写你的课 A 你还不知道接下来会是哪一堂课。只有在您创建了课程之后 C 通过多重继承并运行您的程序,您将获得mro并“知道”下一步将会是什么 A

对于您的示例,它意味着:

C() 打电话给 __init__() 的 C,它称之为 __init__() 的 A。现在, A 使用 super() 并发现 B 在mro,因此它称之为 __init__() 的 B。接下来, __init__() 的 C 打电话给 __init__() 的 B 再次。

调用 super() 在里面 __init__() 创建一个不同的mro并避免双重调用 __init__() 的 B

from __future__ import print_function

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

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

class C(A,B):
    def __init__(self):
        print("C")
        super(C, self).__init__()

使用:

>>> C.mro()
[__main__.C, __main__.A, __main__.B, object]
>> C()
C
A
B

8
2018-02-25 12:18



我还是不明白你能解释得更多吗? - danidee
C的MRO是 ['C', 'A', 'B', 'object']。 C电话 A.__init__(),A电话 super(),在C的MRO(其中 A.__init__() 被称为)下一行是'B',所以 print 'B' 在A的super()和in中调用 B.__init__() - Mr. E


让我们稍微修改一下代码并替换 __init__ 同 doit 只是为了确保行为是通用的,与之无关 __init__

我们还添加更多输出以查看到底发生了什么:

class A(object):
     def doit(self):
         print "A", self, super(A, self)
         super(A, self).doit()

class B(object):
     def doit(self):
         print "B", self, super(B, self)

class C(A,B):
     def doit(self):
         print "C", self
         A.doit(self)
         B.doit(self)

print "MRO:", [x.__name__ for x in C.__mro__]
#MRO: ['C', 'A', 'B', 'object']

C().doit()

这将输出:

C <__main__.C object at ...>
A <__main__.C object at ...> <super: <class 'A'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>

你看,那 self 实际上是 C 对象无处不在,所以当你击中时 A.doit你真的有 <super: <class 'A'>, <C object>>

这转化为:

对象 C 打电话给 doit 之后的下一个(超级)类的方法 A 来自MRO清单

之后是MRO的下一堂课 A 是 B,所以我们最终打电话 B.doit()

检查此代码:

class C(A,B):

     def doit_explain(self):
         print "C", self
         # calls B.doit()
         super(A, self).doit()
         print "Back to C"
         # calls A.doit() (and super in A also calls B.doit())
         super(C, self).doit()
         print "Back to C"
         # and just B.doit()
         B.doit(self)

而不是 A.doit(self) 我用 super(A, self).doit() 直接,它也导致 B.doit() 打电话,这是输出:

C <__main__.C object at ...>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
Back to C
A <__main__.C object at ...> <super: <class 'A'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
Back to C
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 

1
2018-02-26 00:05