Python的“超级”如何做正确的事情?
我正在运行Python 2.5,因此这个问题可能不适用于Python 3.当您使用多重inheritance创build钻石类层次结构并创build派生类的对象时,Python将执行Right Thing(TM)。 它调用派生类的构造函数,然后从左到右列出父类,然后是祖父类。 我熟悉Python的MRO ; 那不是我的问题。 我很好奇从超级对象返回的对象实际上是如何pipe理的,以正确的顺序在父类中调用超级调用。 考虑这个例子代码:
#!/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的初始化函数中注释掉超级调用,那么A和C的init函数都不会被调用。 这意味着B对super的调用在某种程度上意识到C在整个类层次结构中的存在。 我知道super用一个重载的get操作符返回一个代理对象,但是在D的初始化定义中super由super返回的对象如何将B的初始化定义中的C的存在传递给super所返回的对象呢? 是超级用户的后续调用信息存储在对象本身? 如果是这样,为什么不是超级而是self.super?
编辑:Jekke正确地指出,它不是self.super,因为super是类的一个属性,而不是类的一个实例。 从概念上讲,这是有道理的,但实际上超级也不是class级的属性! 你可以在解释器中做两个类A和B,其中Binheritance自A,然后调用dir(B)
。 它没有super
或__super__
属性。
我在下面提供了一系列的链接,它们比我所希望的更详细,更准确地回答你的问题。 不过,我也会用我自己的话来回答你的问题,为你节省一些时间。 我会把它放在点 –
- 超级是一个内build函数,而不是一个属性。
- Python中的每个类 (类)都有一个
__mro__
属性,用于存储特定实例的方法parsing顺序。 - 每个超级调用的forms超级(types[,对象或types])。 让我们假设第二个属性是目前的一个对象。
- 在超级调用的起始点,对象是Derived类( 比如DC )的types。
- super在MRO的类中查找匹配(在你的情况下是
__init__
)的方法,在指定为第一个参数的类(在这种情况下是DC之后的类)之后。 - 当find匹配的方法(比如BC1类)时,它被调用。
(这个方法应该使用超级,所以我假设它 – 看看Python的超级漂亮,但不能使用 – 下面的链接)该方法然后导致search对象的类MRO的下一个方法,右BC1 。 - 冲洗重复,直到所有的方法被发现和调用。
解释你的例子
MRO: D,B,C,A,object
-
super(D, self).__init__()
被调用。 isinstance(self,D)=> True -
在D右侧的课程中searchMRO中的下一个方法
B.__init__
find并调用 -
B.__init__
调用super(B, self).__init__()
。isinstance(self,B)=> False
isinstance(self,D)=> True -
因此,MRO是相同的,但是search继续到B的右边,即C,A,对象被逐一search。 下一个
__init__
被调用。 -
等等等等。
超级的解释
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
使用超级时要注意的事情
http://fuhm.net/super-harmful/
pythonMROalgorithm:
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'>)
另外值得一提的是Python的超级漂亮,但是你不能使用它 。
只是猜测:
所有四种方法中的self
指的是同一个对象,也就是D
类。 所以,在B.__init__()
,对super(B,self)
的调用知道super(B,self)
的全部菱形祖先,并且必须从'after'B中获取方法。 在这种情况下,它是C
类。
super()
知道完整的类层次结构。 这是在B的init中发生的事情:
>>> super(B, self) <super: <class 'B'>, <D object>>
这解决了中心问题,
在D的初始化定义中super由super返回的对象是如何将B的初始化定义中C的存在传递给super所返回的对象的?
也就是说,在B的init定义中, self
是D
一个实例,因此传达了C
的存在。 例如C
可以在type(self).__mro__
。
雅各布的回答显示了如何理解这个问题,而蝙蝠仔细的显示细节,人力资源部门直指点。
有一件事他们没有涉及(至less不是明确的)你的问题是这一点:
但是,如果在B的初始化函数中注释掉超级调用,那么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__()
线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
的右边第一个类的__mro__
,它具有这样一个属性 。
所以,如果你在B的init函数中注释掉超级调用 ,那么方法栈将停在B.__init__
,并且永远不会到达C
或者A
总结:
- 无论哪个类引用它,
self
总是对实例的引用,其__mro__
和__class__
保持不变 - super()find
__mro__
上的当前类的右边的__mro__
。 由于__mro__
保持不变,所发生的是它被作为一个列表search,而不是一棵树或一个graphics。
在最后一点上,请注意,MROalgorithm的全名是C3超类线性化 。 也就是说,它把这个结构变成了一个列表。 当不同的super()
调用发生时,它们将有效地迭代该列表。