Python的“超级”如何做正确的事情?
我正在运行Python 2.5,所以这个问题可能不适用于Python 3.当您使用多继承创建钻石类层次结构并创建派生类的对象时,Python会执行Right Thing(TM)。 它调用派生类的构造函数,然后从左到右列出父类,然后是祖父类。 我熟悉Python的MRO; 那不是我的问题。 我很好奇从超级对象返回的对象实际上是如何与父类中的超级调用进行通信的。 考虑这个示例代码:
#!/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所返回的对象呢? 超级使用的后续调用是否存储在对象本身上的信息? 如果是这样,为什么不是super而是self.super?
编辑:Jekke非常正确地指出,它不是self.super,因为super是类的一个属性,而不是类的一个实例。 从概念上讲,这是有道理的,但实际上超级并不是这个班的属性! 你可以在解释器中做两个类A和B,其中B从A继承,然后调用dir(B)
。 它没有super
或__super__
属性。
我在下面提供了一堆链接,它们比我所希望的更详细和更精确地回答您的问题。 然而,我也会用我自己的话来回答你的问题,为你节省一些时间。 我会把它放在点 -
__mro__
属性,用于存储特定实例的方法解析顺序。 __init__
)。 (这种方法应该使用super,所以我假设它的确如此 - 请参阅Python的super很漂亮,但不能使用 - 下面的链接)然后,该方法会导致在对象类的MRO中搜索下一个方法的MRO BC1 。
你的例子的解释
MRO: D,B,C,A,object
super(D, self).__init__()
被调用。 isinstance(self,D)=> True 在D右侧的课程中搜索MRO中的下一个方法
B.__init__
找到并调用
B.__init__
调用super(B, self).__init__()
。
isinstance(self,B)=> False
isinstance(self,D)=> True
因此,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
我希望这有助于清除它。
把你的代码__mro__
这个,我想它会解释一些事情(大概是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的Super很漂亮,但你不能使用它。
只是猜测:
所有四种方法中的self
指的是同一个对象,即D
类。 所以,在B.__init__()
调用到super(B,self)
知道整个钻石血统self
,它有“后”来去方法B
。 在这种情况下,它是C
类。