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__属性。


我在下面提供了一堆链接,它们比我所希望的更详细和更精确地回答您的问题。 然而,我也会用我自己的话来回答你的问题,为你节省一些时间。 我会把它放在点 -

  • super是内建函数,而不是属性。
  • Python中的每个类型(类)都有一个__mro__属性,用于存储特定实例的方法解析顺序。
  • 每个超级调用的形式都是super(type [,object-or-type])。 让我们假设第二个属性是目前的一个对象。
  • 在超级调用的起始点,该对象是Derived类( 比如DC )的类型。
  • super在类指定为第一个参数(在本例中为DC之后的类)之后查找MRO中的类中匹配的方法(在您的情况下为__init__ )。
  • 当找到匹配的方法时(比如在BC1类中),它被调用。
    (这种方法应该使用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类。

    链接地址: http://www.djcxy.com/p/26783.html

    上一篇: How does Python's "super" do the right thing?

    下一篇: Python divide odd length string and combine