31

super()直接使用和直接使用父类名有区别吗?例如:

class Parent:
    def __init__(self):
        print("In parent")
        self.__a=10

class Child(Parent):
    def __init__(self):
        super().__init__()     # using super()
        Parent.__init__(self)  # using Parent class name

c=Child()

super().__init__()和内部有区别Parent.__init__(self)吗?

4

2 回答 2

21

在这种情况下不是。但通常,特别是当您使用多重继承时,super()委托给方法解析顺序 (MRO)中的下一个对象,如文档中所指定:

super([type[, object-or-type]])

返回一个代理对象,它将方法调用委托给类型的父类或同级类。这对于访问已在类中重写的继承方法很有用。搜索顺序与 by 使用的getattr()相同,只是跳过了类型本身。

type的__mro__属性列出了getattr()和使用的方法解析搜索顺序super()。该属性是动态的,并且可以在更新继承层次结构时更改。

(...)

(已复制,加粗体)

比如说你定义了像这样的类(借用这个问题,其中更详细地讨论了 MRO):

class F:
    def __init__(self):
        print('F%s'%super().__init__)
        super().__init__()

class G: 
    def __init__(self):
        print('G%s'%super().__init__)
        super().__init__() 

class H: 
    def __init__(self):
        print('H%s'%super().__init__)
        super().__init__()

class E(G,H):
    def __init__(self):
        print('E%s'%super().__init__)
        super().__init__()

class D(E,F): 
    def __init__(self):
        print('D%s'%super().__init__)
        super().__init__() 

class C(E,G): 
    def __init__(self):
        print('C%s'%super().__init__)
        super().__init__()

class B(C,H): 
    def __init__(self):
        print('B%s'%super().__init__)
        super().__init__()

class A(D,B,E): 
    def __init__(self):
        print('A%s'%super().__init__)
        super().__init__()

那么__mro__ofA是:

A.__mro__ == (A,D,B,C,E,G,H,F,object)

现在,如果我们调用A(),它会打印:

A<bound method D.__init__ of <__main__.A object at 0x7efefd8645c0>>
D<bound method B.__init__ of <__main__.A object at 0x7efefd8645c0>>
B<bound method C.__init__ of <__main__.A object at 0x7efefd8645c0>>
C<bound method E.__init__ of <__main__.A object at 0x7efefd8645c0>>
E<bound method G.__init__ of <__main__.A object at 0x7efefd8645c0>>
G<bound method H.__init__ of <__main__.A object at 0x7efefd8645c0>>
H<bound method F.__init__ of <__main__.A object at 0x7efefd8645c0>>
F<method-wrapper '__init__' of A object at 0x7efefd8645c0>
<__main__.A object at 0x7efefd8645c0>

所以这意味着在试图获得它的上下文中A和时:__init__

  • super().__init__AD.__init__; _
  • super().__init__DB.__init__; _
  • super().__init__BC.__init__; _
  • super().__init__CE.__init__; _
  • super().__init__EG.__init__; _
  • super().__init__GH.__init__; _
  • super().__init__HF.__init__; _ 和
  • super().__init__Fobject.__init__。_

因此请注意,它本身super()并不委托给 parent。例如super()ofDB并且B不是 的超类D,所以它实际上取决于对象的类型(而不是类)。

现在在 的情况下D__mro__是:

D.__mro__ = (D,E,G,H,F,object)

如果我们构造 aD但是我们得到:

D<bound method E.__init__ of <__main__.D object at 0x7efefd864630>>
E<bound method G.__init__ of <__main__.D object at 0x7efefd864630>>
G<bound method H.__init__ of <__main__.D object at 0x7efefd864630>>
H<bound method F.__init__ of <__main__.D object at 0x7efefd864630>>
F<method-wrapper '__init__' of D object at 0x7efefd864630>

所以在它的上下文中D认为:

  • super().__init__DE.__init__; _
  • super().__init__EG.__init__; _
  • super().__init__GH.__init__; _
  • super().__init__HF.__init__; _ 和
  • super().__init__Fobject.__init__。_

所以这里super()ofD导致E(for __init__)这在 的上下文中A是不一样的。

于 2017-02-23T11:00:23.277 回答
5
super().__init__(*args, **kwargs)   

意识到你没有传递“自我”——它是自动插入的。

super()最初是在 Python 2 中设计的,允许类在类层次结构中作为 mixin 重用,其直接超类可能会改变:

让我们假设在某个时间点您的代码是这样的:

class A: pass
class B(A): 
    def __init__(self, *args, **kwargs):
          ...
          # Fixed call to A
          A.__init__(self, *args, **kwargs)

class C(A):
    def __init__(self, *args, **kwargs):
          ...
          # Fixed call to A
          A.__init__(self, *args, **kwargs)

class D(C, B): 
    pass

此时,应该执行正确的 OOP 代码,C.__init__ 该代码应该将调用链接到B.__init__: 但是当超类名称被硬编码时,不会发生 -A总是__init__接下来。而且,如果您B.__init__在 中进行硬编码C,您将C无法在没有 的情况下工作B,从而违背了多重继承的目的。

当您super()改为使用时,Python 会根据类的__mro__属性执行下一个父类的方法搜索(mro = 方法解析顺序。__mro__是附加到每个 Python 类的具体属性)。- 因此,如果在某个时间点,D上面的类不再继承自,对 inB的调用将自动重新路由到.super().__init__CA

还值得注意的是,在 Python 3 中super引入了无参数形式的 以简化其使用 - 在此之前,必须硬编码对自己类的引用并插入self参数。这种形式是 Python 中为数不多的在编译器本身中硬编码的例外之一 - 当在方法主体中看到super(或) 时,它会在方法内部更改内容(即,它创建一个指向调用的类本身的变量用途)__class____class__super

于 2017-03-16T12:43:30.270 回答