我正在运行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返回的对象?后续超级使用调用的信息是否存储在对象本身上?如果是这样,为什么不是超级而不是self.super?
编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例。从概念上讲,这是有道理的,但在实践中,超级也不是班级的属性!您可以在解释器中通过创建两个类A和B来测试它,其中B继承自A,并调用dir(B)
。它没有super
或__super__
属性。
我在下面提供了一系列链接,它们比我希望的更详细,更准确地回答您的问题。不过,我会用自己的话回答你的问题,为你节省一些时间。我会说点 -
__mro__
属性,用于存储该特定实例的方法解析顺序。__init__
)的方法。您的示例的说明
MRO: D,B,C,A,object
super(D, self).__init__()
被称为。 isinstance(self,D)=>真B.__init__
找到并打电话给B.__init__
称super(B, self).__init__()
。
isinstance(self,B)=> False
isinstance(self,D)=>真__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
我希望这有助于澄清它。
将你的代码更改为此,我认为它会解释一些事情(大概是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'>)
只是猜测:
所有四种方法中的self
指的是同一个对象,即类D
。因此,在B.__init__()
,对super(B,self)
的召唤知道self
的整个钻石血统,它必须从'之后'B
获取方法。在这种情况下,它是C
类。
super()
知道完整的类层次结构。这是B的init中发生的事情:
>>> super(B, self)
<super: <class 'B'>, <D object>>
这解决了核心问题,
super在D的init定义中返回的对象如何将C的存在传达给B的init定义中super返回的对象?
也就是说,在B的初始定义中,self
是D
的一个实例,因此传达了C
的存在。例如,C
可以在type(self).__mro__
找到。
Jacob的答案显示了如何理解这个问题,而batbrat则显示了细节,而hrr则直截了当。
从这个问题来看,他们没有涵盖(至少没有明确表达)的一点是:
但是,如果你在B的init函数中注释掉对super的调用,则不会调用A和C的init函数。
要理解这一点,请将Jacob的代码更改为在A的init上打印堆栈,如下所示:
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__()
,因为C
不是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()
发生这种情况是因为super (B, self)
并没有“调用B的基础版__init__
”。相反,它是在__init__
的B
上存在的self
右边的第一类调用__mro__
并且具有这样的属性。
因此,如果你在B的init函数中注释掉对super的调用,那么方法堆栈将在B.__init__
上停止,并且永远不会到达C
或A
。
总结一下:
self
始终是对实例的引用,其__mro__
和__class__
保持不变__mro__
上当前类右边的类的方法。由于__mro__
保持不变,所发生的是它被搜索为列表,而不是树或图形。在最后一点,请注意MRO算法的全名是C3超类线性化。也就是说,它将该结构展平为一个列表。当不同的super()
调用发生时,它们有效地迭代该列表。