How does Python's “super” do the right thing?
我运行的是python 2.5,所以这个问题可能不适用于python 3。当您使用多重继承创建菱形类层次结构并创建派生的大多数类的对象时,Python会做正确的事情(tm)。它调用派生的大多数类的构造函数,然后调用从左到右列出的父类,然后调用祖父母类。我熟悉python的mro,这不是我的问题。我很好奇从super返回的对象是如何以正确的顺序与父类中super的调用进行通信的。考虑下面的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/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() |
代码做直观的事情,它打印:
1 2 3 4 | 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 use的信息是否存储在对象本身上?如果是这样,为什么不是超级的自我。超级的?
编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例。从概念上讲,这是有意义的,但实际上super也不是类的属性!您可以在解释器中测试这一点,方法是创建两个类A和B,其中B从A继承,并调用
改变你的密码,我想它会解释的事情(以前是EDOCX1&7)。Is looking at where,say,EDOCX1&5)页:1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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() |
如果你运行它,你会看到
ZZU1
也值得检查Python的超级牛,但你不能用它。
我在下面提供了一些链接,这是你的问题的更详细和更精确的答案。我会给你一个答案,在我自己的话语中,这样你就有时间了。我会把它放进去
以你为例
1 | MRO: D,B,C,A,object |
搜索下一个方法在MRO中的阶级到D的权利。
找到并呼唤
埃多克斯1
自我补偿自我评价
Thus,the MRO is the same,but the search continues to the right of B.I.E.C,A,object are searched one by one.下一个被称为
所以就这样
超级明星http://www.python.org/download/releases/2.2.3/descrintro/ 350;cooperation当使用超级http://fuhm.net/super-harmful/。PythonMRO算法http://www.python.org/download/releases/2.3/mro/。好文件http://docs.python.org/library/functions.htmlThe bottom of this page has a nice section on super:http://docstore.mik.ua/orelly/other/python/0596001886 pythonian-chp-5-sect-2.html
我希望这将帮助清晰。
只是警告:
在所有四种方法中,都参照同样的目标,这是一个等级的
知道所有阶级的等级。这就是B's initial:
1 2 | >>> super(B, self) <super: <class 'B'>, <D object>> |
This resolves the central question,
how does the object returned by super in D's init definition communicate the existence of C to the object returned by super in B's init definition?
NAMELY,in B's initial definition,
雅各布的回答说明了如何理解这个问题,而巴布拉特的回答则说明了细节,而人力资源报告则直截了当地说明了问题所在。
他们没有从你的问题中涵盖(至少不是明确的)一件事是这一点:
However, if you comment out the call to super in B's init function, neither A nor C's init function is called.
要理解这一点,请将jacob的代码更改为在a的init上打印堆栈,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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() |
当
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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() |
这是因为
因此,如果您在b的init函数中注释掉对super的调用,方法堆栈将在
总结:
- 无论哪个类引用它,
self 总是引用实例,其__mro__ 和__class__ 保持不变。 - super()查找查找
__mro__ 上当前类右侧的类的方法。当__mro__ 保持不变时,会发生的情况是它被搜索为一个列表,而不是树或图。
最后一点,注意MRO算法的全名是c3超类线性化。也就是说,它将该结构扁平化为一个列表。当不同的