关于构造函数:Python的”super”如何做正确的事情?

How does Python's “super” do the right thing?

我运行的是python 2.5,所以这个问题可能不适用于python 3。当您使用多重继承创建菱形类层次结构并创建派生的大多数类的对象时,Python会做正确的事情(tm)。它调用派生的大多数类的构造函数,然后调用从左到右列出的父类,然后调用祖父母类。我熟悉python的mro,这不是我的问题。我很好奇从super返回的对象是如何以正确的顺序与父类中super的调用进行通信的。考虑下面的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/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()

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

1
2
3
4
D init
B init
C init
A init

但是,如果您注释了对b的init函数中super的调用,则不会调用a或c的init函数。这意味着B对super的调用不知何故地意识到C在整个类层次结构中的存在。我知道super返回一个带有重载get运算符的代理对象,但是d的init定义中super返回的对象如何将c的存在与b的init定义中super返回的对象通信?随后调用super use的信息是否存储在对象本身上?如果是这样,为什么不是超级的自我。超级的?

编辑:Jekke非常正确地指出它不是self.super,因为super是类的属性,而不是类的实例。从概念上讲,这是有意义的,但实际上super也不是类的属性!您可以在解释器中测试这一点,方法是创建两个类A和B,其中B从A继承,并调用dir(B)。它没有super__super__属性。


改变你的密码,我想它会解释的事情(以前是EDOCX1&7)。Is looking at where,say,EDOCX1&5)页:1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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()

如果你运行它,你会看到

ZZU1

也值得检查Python的超级牛,但你不能用它。


我在下面提供了一些链接,这是你的问题的更详细和更精确的答案。我会给你一个答案,在我自己的话语中,这样你就有时间了。我会把它放进去

  • 超级是一个建筑功能,而不是一个属性。
  • 在Python中,每一种类型(类)都有一个__mro__属性,该属性使百叶窗的方法分辨出这一特定实例的次序。
  • 每个呼叫超级的都是超级形式(类型[,对象或类型])。让我们假设第二个属性目前是一个对象。
  • 在超级呼叫的起始点,所述对象为来自类别的类型(say DC)。
  • 超级看待比赛的方法(在你的__init__的情况下)在MRO的班级中,作为第一个论据(在此处,在DC之后的班级中)。
  • 当匹配方法被发现时(BC1类中说),它被称为匹配方法。(这个方法应该使用超级,所以我认为它是——看Python的超级是巢穴,但不能在下面使用)这一方法在物体的MRO级下一种方法中导致了对BC1权利的搜索。
  • Rinse wash repeat till all the methods are found and called.
  • 以你为例

    1
     MRO: D,B,C,A,object
  • 被召唤。自我评价
  • 搜索下一个方法在MRO中的阶级到D的权利。

    找到并呼唤

  • 埃多克斯1

    自我补偿自我评价

  • Thus,the MRO is the same,but the search continues to the right of B.I.E.C,A,object are searched one by one.下一个被称为

  • 所以就这样

  • 超级明星http://www.python.org/download/releases/2.2.3/descrintro/ 350;cooperation当使用超级http://fuhm.net/super-harmful/。PythonMRO算法http://www.python.org/download/releases/2.3/mro/。好文件http://docs.python.org/library/functions.htmlThe bottom of this page has a nice section on super:http://docstore.mik.ua/orelly/other/python/0596001886 pythonian-chp-5-sect-2.html

    我希望这将帮助清晰。


    只是警告:

    在所有四种方法中,都参照同样的目标,这是一个等级的D。因此,在B.__init__()中,要求super(B,self)知道self的整个钻石序列,并从B后的方法中消失。在本案中,这是C级。


    知道所有阶级的等级。这就是B's initial:

    1
    2
    >>> super(B, self)
    <super: <class 'B'>, <D object>>

    This resolves the central question,

    how does the object returned by super in D's init definition communicate the existence of C to the object returned by super in B's init definition?

    NAMELY,in B's initial definition,selfis an instance of D,and thus communicates the existence of CC为例,可在type(self).__mro__中找到。


    雅各布的回答说明了如何理解这个问题,而巴布拉特的回答则说明了细节,而人力资源报告则直截了当地说明了问题所在。

    他们没有从你的问题中涵盖(至少不是明确的)一件事是这一点:

    However, if you comment out the call to super in B's init function, neither A nor C's init function is called.

    要理解这一点,请将jacob的代码更改为在a的init上打印堆栈,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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()

    C不是B的基类时,看到B的行super(B, self).__init__()实际上在调用C.__init__(),有点令人惊讶。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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()

    这是因为super (B, self)不是"调用b的__init__的基类版本"。相反,它是"调用__init__",位于self__mro__上存在的B的权利的第一类,并且具有这样的属性。

    因此,如果您在b的init函数中注释掉对super的调用,方法堆栈将在B.__init__上停止,并且永远不会到达CA

    总结:

    • 无论哪个类引用它,self总是引用实例,其__mro____class__保持不变。
    • super()查找查找__mro__上当前类右侧的类的方法。当__mro__保持不变时,会发生的情况是它被搜索为一个列表,而不是树或图。

    最后一点,注意MRO算法的全名是c3超类线性化。也就是说,它将该结构扁平化为一个列表。当不同的super()调用发生时,它们会有效地迭代该列表。