Python的"超级"如何做正确的事情?

Jos*_*vin 51 python constructor multiple-inheritance super python-2.5

我正在运行Python 2.5,因此这个问题可能不适用于Python 3.当您使用多重继承创建钻石类层次结构并创建派生最多类的对象时,Python会执行Right Thing(TM).它调用派生最多类的构造函数,然后调用从左到右列出的父类,然后是祖父母.我熟悉Python的MRO ; 那不是我的问题.我很好奇从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()
Run Code Online (Sandbox Code Playgroud)

代码做直观的事情,它打印:

D init
B init
C init
A init
Run Code Online (Sandbox Code Playgroud)

但是,如果你在B的init函数中注释掉对super的调用,则不会调用A和C的init函数.这意味着B对super的调用在某种程度上意识到C在整个类层次结构中的存在.我知道super返回一个带有重载get运算符的代理对象,但是在D的init定义中super返回的对象如何将C的存在传达给B的init定义中super返回的对象?后续超级使用调用的信息是否存储在对象本身上?如果是这样,为什么不是超级而不是self.super?

编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例.从概念上讲,这是有道理的,但在实践中,超级也不是班级的属性!您可以在解释器中通过创建两个类A和B来测试它,其中B继承自A,并且调用dir(B).它没有super__super__属性.

Jac*_*son 34

更改您的代码这一点,我认为它会解释的东西(可能是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()
Run Code Online (Sandbox Code Playgroud)

如果你运行它,你会看到:

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'>)
Run Code Online (Sandbox Code Playgroud)

另外值得一看的是Python的Super是漂亮的,但你不能使用它.

  • 原因是__mro__是由元类定义的,而不是类,因此它不会显示在dir中(参见http://mail.python.org/pipermail/python-dev/2008-March/077604.html) . (3认同)
  • 正如我猜测的那样:自我一直都是D级. (2认同)

bat*_*rat 15

我在下面提供了一系列链接,它们比我希望的更详细,更准确地回答您的问题.不过,我会用自己的话回答你的问题,为你节省一些时间.我会说点 -

  1. super是内置函数,不是属性.
  2. Python中的每个类型(类)都有一个__mro__属性,用于存储该特定实例的方法解析顺序.
  3. 每次调用super都是super(type [,object-or-type]).让我们假设第二个属性是当下的对象.
  4. 在超级调用的起始点,该对象属于Derived类的类型(比如DC).
  5. __init__在指定为第一个参数的类(在本例中为DC之后的类)之后,super会在MRO的类中查找匹配(在您的情况下)的方法.
  6. 当找到匹配方法时(例如在类BC1中),它被调用.
    (这个方法应该使用super,所以我假设它 - 看看Python的超级很漂亮但不能使用 - 下面的链接)然后该方法导致在对象的类'MRO中搜索下一个方法,在右侧BC1.
  7. 冲洗洗涤重复,直到找到并调用所有方法.

您的示例的说明

 MRO: D,B,C,A,object  
Run Code Online (Sandbox Code Playgroud)
  1. super(D, self).__init__()叫做.isinstance(self,D)=>真
  2. 在D的右侧的类中搜索MRO中的下一个方法.

    B.__init__ 找到并打电话


  1. B.__init__电话 super(B, self).__init__().

    isinstance(self,B)=> False
    isinstance(self,D)=> True

  2. 因此,MRO是相同的,但搜索继续到B的右边,即C,A,逐个搜索对象.下一个__init__发现被称为.

  3. 等等等等.

超级
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/
super的文档:
http ://docs.python.org/library/functions.html
这个页面的底部有一个很好的超级部分:
http:/ /docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

我希望这有助于澄清它.

  • 在您的解释中,我不明白第三点“ isinstance(self,B)=&gt; False”,那么为什么继续搜索? (2认同)

Jav*_*ier 6

只是猜测:

self在所有四种方法中都引用同一个对象,即类D.所以,在B.__init__()调用中super(B,self)知道整个钻石的祖先,self它必须从'之后'获取方法B.在这种情况下,它是C班级.