我正在运行Python 2.5,因此这个问题可能不适用于Python 3.当您使用多重继承创建钻石类层次结构并创建派生最多类的对象时,Python会执行Right Thing(TM)。它调用派生最多类的构造函数,然后调用从左到右列出的父类,然后是祖父母。我熟悉Python的MRO;那不是我的问题。我很好奇从super返回的对象实际上如何管理与父类中的super调用正确的顺序进行通信。请考虑以下示例代码:
#!/usr/bin/python
class A(object):
def __init__(self): print "A init"
class B(A):
def __init__(self):
print "B init"
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
super(D, self).__init__()
x = D()
代码执行直观的操作,它打印出来:
D init
B init
C init
A init
但是,如果在B的init函数中注释掉对super的调用,则不会调用A和C的init函数。这意味着B对super的调用在某种程度上意识到C在整个类层次结构中的存在。我知道super返回一个带有重载get运算符的代理对象,但是在D的init定义中super返回的对象如何将C的存在传达给B的init定义中super返回的对象?后续超级使用调用的信息是否存储在对象本身上?如果是这样,为什么不是super而不是self.super?
编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例。从概念上讲,这是有道理的,但在实践中,超级也不是班级的属性!您可以在解释器中通过创建两个类A和B来测试它,其中B继承自A,并调用dir(B)
。它没有super
或__super__
属性。
答案 0 :(得分:34)
将您的代码更改为此,我认为它会解释一些事情(大概super
正在查看B
中的__mro__
在哪里?)
class A(object):
def __init__(self):
print "A init"
print self.__class__.__mro__
class B(A):
def __init__(self):
print "B init"
print self.__class__.__mro__
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
print self.__class__.__mro__
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
print self.__class__.__mro__
super(D, self).__init__()
x = D()
如果你运行它,你会看到:
D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
答案 1 :(得分:15)
我在下方提供了一系列链接,可以更详细地回答您的问题,而且比我希望的更准确。不过,我会用自己的话回答你的问题,为你节省一些时间。我会把它分成几点 -
__mro__
属性,用于存储该特定实例的方法解析顺序。__init__
)。 您的示例说明
MRO: D,B,C,A,object
super(D, self).__init__()
被调用。 isinstance(self,D)=&gt;真的在D右侧的课程中搜索MRO中的下一个方法。
B.__init__
找到并致电
B.__init__
来电super(B, self).__init__()
。
isinstance(self,B)=&gt;假
isinstance(self,D)=&gt;真
因此,MRO是相同的,但是搜索继续到B的右边,即C,A,逐个搜索对象。找到下一个__init__
。
依此类推。
超级的解释
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
使用超级时要注意的事项
http://fuhm.net/super-harmful/
Pythons MRO算法:
http://www.python.org/download/releases/2.3/mro/
超级博士:
http://docs.python.org/library/functions.html
本页底部有一个很好的超级部分:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
我希望这有助于澄清它。
答案 2 :(得分:6)
猜猜:
所有四种方法中的 self
都引用同一个对象,即类D
。
因此,在B.__init__()
中,对super(B,self)
的调用知道self
的整个钻石祖先,并且必须从'{'B
之后获取方法。在这种情况下,它是C
类。
答案 3 :(得分:3)
super()
知道完整类层次结构。这是B的init中发生的事情:
>>> super(B, self)
<super: <class 'B'>, <D object>>
这解决了核心问题,
super在D的init定义中返回的对象如何将C的存在与B的init定义中的super返回的对象进行通信?
即,在B的init定义中,self
是D
的实例,因此传达了C
的存在。例如C
可以找到type(self).__mro__
。
答案 4 :(得分:2)
他们在你的问题中没有涵盖(至少没有明确)的一点是这一点:
但是,如果你在B的init函数中注释掉对super的调用,则不会调用A和C的init函数。
要理解这一点,请将Jacob的代码更改为在A&lt; s init上打印堆栈,如下所示:
lbl_btn
有点令人惊讶的是,import traceback
class A(object):
def __init__(self):
print "A init"
print self.__class__.__mro__
traceback.print_stack()
class B(A):
def __init__(self):
print "B init"
print self.__class__.__mro__
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
print self.__class__.__mro__
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
print self.__class__.__mro__
super(D, self).__init__()
x = D()
的行B
实际上正在调用super(B, self).__init__()
,因为C.__init__()
不是{{1}的基类}}
C
这是因为B
没有&#39; 调用B的D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
File "/tmp/jacobs.py", line 31, in <module>
x = D()
File "/tmp/jacobs.py", line 29, in __init__
super(D, self).__init__()
File "/tmp/jacobs.py", line 17, in __init__
super(B, self).__init__()
File "/tmp/jacobs.py", line 23, in __init__
super(C, self).__init__()
File "/tmp/jacobs.py", line 11, in __init__
traceback.print_stack()
&#39;的基类版本。相反,它是在super (B, self)
__init__
上出现的__init__
右侧的第一个类上调用B
并且具有这样的属性。
因此,如果您在B&lt; s&gt;的初始化函数中注释掉对super的调用,方法堆栈将停在self
上,永远不会到达__mro__
或B.__init__
。
总结:
C
始终是对实例的引用,其A
和self
保持不变__mro__
上当前类右侧的类。由于__class__
保持不变,所以会将其作为列表进行搜索,而不是作为树或图形进行搜索。关于最后一点,请注意MRO算法的全名是 C3超类线性化。也就是说,它将该结构展平为一个列表。当发生不同的__mro__
调用时,它们会有效地迭代该列表。