Python';s";超级";做正确的事?
我正在运行Python2.5,所以这个问题可能不适用于Python3。当您使用多重继承创建菱形类层次结构并创建派生most类的对象时,Python做了正确的事情(TM)。它调用派生most类的构造函数,然后调用从左到右列出的父类,然后调用祖辈类。我熟悉Python的;这不是我的问题。我很好奇,从super返回的对象实际上是如何以正确的顺序与父类中的super调用通信的。考虑这个示例代码:Python';s";超级";做正确的事?,python,constructor,multiple-inheritance,super,python-2.5,Python,Constructor,Multiple Inheritance,Super,Python 2.5,我正在运行Python2.5,所以这个问题可能不适用于Python3。当您使用多重继承创建菱形类层次结构并创建派生most类的对象时,Python做了正确的事情(TM)。它调用派生most类的构造函数,然后调用从左到右列出的父类,然后调用祖辈类。我熟悉Python的;这不是我的问题。我很好奇,从super返回的对象实际上是如何以正确的顺序与父类中的super调用通信的。考虑这个示例代码: #!/usr/bin/python class A(object): def __init__(s
#!/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返回的对象如何与B的init定义中super返回的对象通信C的存在?超级用户的后续调用所使用的信息是否存储在对象本身上?如果是这样,为什么不是super而不是self.super
编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例。从概念上讲,这是有道理的,但实际上super也不是类的属性!您可以在解释器中测试这一点,方法是创建两个类A和B,其中B继承自A,并调用dir(B)
。它没有super
或\uuu super\uuuu
属性。只是猜测:
self
在所有四种方法中都引用相同的对象,即D类的对象。
因此,在B.\uu init\uu()
中,对super(B,self)
的调用知道self
的整个钻石祖先,它必须从'after'B
获取方法。在本例中,它是C
类。将您的代码更改为该类,我认为它会解释一些事情(大概super
正在查看\mro\uuuu
中的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'>)
D初始化
(, , )
从头算
(, , )
从头算
(, , )
初始化
(, , )
也值得一看。我在下面提供了一系列链接,这些链接比我所希望的更详细、更准确地回答了您的问题。不过,为了节省你们的时间,我也会用我自己的话回答你们的问题。我将把它分为几点-
super是一个内置函数,而不是属性
Python中的每个类型(类)都有一个\uuuumro\uuuu
属性,该属性存储该特定实例的方法解析顺序
每个对super的调用都是super(type[,object或type])的形式。让我们假设第二个属性目前是一个对象
在超级调用的起点,对象是派生类的类型(比如说DC)
super在指定为第一个参数的类(在本例中,类在DC之后)之后,在MRO中的类中查找匹配的方法(在您的示例中为\uu init\uu
)李>
当找到匹配方法时(例如在类BC1中),将调用它。
(这个方法应该使用super,所以我假设它会使用——请看Python的super很漂亮,但不能使用——下面的链接)
然后,该方法在对象的类“MRO”中搜索下一个方法,位于BC1的右侧李>
反复冲洗,直到找到并调用所有方法
对示例的解释
MRO: D,B,C,A,object
super(D,self)。\uuu init\uuuu()
被调用。isinstance(self,D)=>真
在D右侧的类中的MRO中搜索下一个方法
B.\uuuu init\uuuu
找到并调用
B.。\uuuu init\uuuu
调用super(B,self)。\uuuu init\uuuu()
isinstance(self,B)=>False
isinstance(self,D)=>真
因此,MRO是相同的,但搜索继续到B的右侧,即C、A、对象逐个搜索。调用找到的下一个\uuuuu init\uuuu
等等等等
对super的解释
使用super时要注意的事项
蟒蛇MRO算法:
super的文档:
本页底部有一个很好的超级部分:
我希望这有助于澄清问题。super()
知道完整的类层次结构。这是在B的init中发生的情况:
>>> super(B, self)
<super: <class 'B'>, <D object>>
超级(B,自我)
这就解决了中心问题,
D的init定义中super返回的对象如何将C的存在与B的init定义中super返回的对象进行通信
也就是说,在B的init定义中,
self
是D
的一个实例,因此传达了C
的存在。例如,C
可以在type(self)中找到。\umro\uuuu
雅各布的答案显示了如何理解问题,而巴特布拉特的答案显示了细节,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)。\uuu init\uu()
实际上在调用C.\uu init\uu()
,有点令人惊讶,因为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()
D初始化
(, , )
从头算
(, , )
从头算
(, , )
初始化
(, , )
文件“/tmp/jacobs.py”,第31行,
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()