我熟悉Python的MRO; 's not my question. I'好奇如何从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非常正确地指出它也是该类的一个属性!您可以在解释器中通过创建两个类A和B来测试它,其中B继承自A,并调用 dir(B)
. 它没有 super
或 __super__
属性 .
5 回答
我在下面提供了一系列链接,它们比我希望的更详细,更准确地回答您的问题 . 不过,我会用自己的话回答你的问题,为你节省一些时间 . 我会说点 -
super是内置函数,不是属性 .
Python中的每个类型(类)都有一个
__mro__
属性,用于存储该特定实例的方法解析顺序 .每次调用super的形式都是super(type [,object-or-type]) . 让我们假设第二个属性是当下的对象 .
在超级调用的起始点,该对象属于Derived类的类型( say DC ) .
super在指定为第一个参数的类(在本例中为DC之后的类)之后,在MRO的类中查找匹配(在您的情况下为
__init__
)的方法 .当找到匹配方法时(例如在类 BC1 中),将调用它 .
(这个方法应该使用super,所以我假设它 - 请参阅Python 's super is nifty but can' t使用 - 链接如下)然后该方法导致在对象's class' MRO中搜索下一个方法,在 BC1 的右侧 .
冲洗重复洗涤,直到找到并调用所有方法 .
Explanation for your example
super(D, self).__init__()
被调用 . isinstance(self,D)=>真在D右侧的 class 中搜索MRO中的 next method .
B.__init__
找到并打电话B.__init__
来电super(B, self).__init__()
.isinstance(self,B)=> False
isinstance(self,D)=>真
因此,MRO是相同的,但搜索继续到B的右边,即C,A,逐个搜索对象 . 找到下一个
__init__
被调用 .依旧等等 .
An explanation of super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Things to watch for when using super
http://fuhm.net/super-harmful/
Pythons MRO Algorithm:
http://www.python.org/download/releases/2.3/mro/
super's docs:
http://docs.python.org/library/functions.html
The bottom of this page has a nice section on super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
我希望这有助于澄清它 .
将你的代码更改为此,我认为它会解释事情(大概是
super
正在查看B
在_1372982中的位置?):如果你运行它,你会看到:
还值得一试Python's Super is nifty, but you can't use it .
只是猜测:
所有四种方法中的
self
引用同一个对象,即类D
. 所以,在B.__init__()
中,对super(B,self)
的调用知道self
的整个钻石祖先,它必须从'after'B
获取该方法 . 在这种情况下,它是C
类 .super()
知道 full 类层次结构 . 这是B的init中发生的事情:这解决了核心问题,
即,在B的init定义中,
self
是D
的实例,因此传达了C
的存在 . 例如C
可以在type(self).__mro__
中找到 .Jacob的答案显示了如何理解这个问题,而batbrat则显示了细节,而hrr则直截了当 .
从这个问题来看,他们没有涵盖(至少没有明确)的一点是:
要理解这一点,请将Jacob的代码更改为在A的init上打印堆栈,如下面:
看到
B
的行super(B, self).__init__()
实际上正在调用C.__init__()
,因为C
不是B
的基类,这有点令人惊讶 .发生这种情况是因为
super (B, self)
没有'调用__init__
'. Instead, it is '的B的基类版本'. Instead, it is ',在B
的B
右边的第一个类上调用__init__
并且具有这样的属性 .所以,如果你在B的init函数中注释掉对super的调用,那么方法堆栈将在
B.__init__
上停止,并且永远不会达到C
或A
.总结一下:
无论哪个类引用它,
self
始终是对实例的引用,其__mro__
和__class__
保持不变super()查找查找
__mro__
上当前类右侧的类的方法 . 由于__mro__
保持不变,所以会发生的事情是将其作为列表进行搜索,而不是作为树或图形进行搜索 .在最后一点,请注意MRO算法的全名是C3超类线性化 . 也就是说,它将该结构展平为一个列表 . 当发生不同的
super()
调用时,它们会有效地迭代该列表 .