Python';s";超级";做正确的事?

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

我正在运行Python2.5,所以这个问题可能不适用于Python3。当您使用多重继承创建菱形类层次结构并创建派生most类的对象时,Python做了正确的事情(TM)。它调用派生most类的构造函数,然后调用从左到右列出的父类,然后调用祖辈类。我熟悉Python的;这不是我的问题。我很好奇,从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返回的对象如何与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()