super() 和 Parent class 名称有什么区别?

What's the difference between super() and Parent class name?

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

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) 内部有区别吗?

不在这种情况下。但是一般,尤其是当你使用多重继承时,super()委托给下一个object 在 方法解析顺序 (MRO) 中,如 documentation:

中指定

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

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.

The __mro__ attribute of the type lists the method resolution search order used by both getattr() and super(). The attribute is dynamic and can change whenever the inheritance hierarchy is updated.

(...)

(已复制,已添加粗体)

比如你定义了 classes(借自 ):

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__()

那么A__mro__就是:

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__ 时:

    A
  • super().__init__D.__init__;
  • super().__init__ of DB.__init__;
  • B
  • super().__init__C.__init__;
  • C
  • super().__init__E.__init__;
  • E
  • super().__init__G.__init__;
  • G
  • super().__init__H.__init__;
  • H
  • super().__init__F.__init__;和
  • F
  • super().__init__object.__init__

因此请注意,super() 本身并不代表 parent。例如 Dsuper()BB 不是 D 的超级 class,所以它确实是 取决于 object 的类型(而不是 class)。

现在在 D 的情况下,__mro__ 是:

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

如果我们构造一个 D 然而我们得到:

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__ of DE.__init__;
  • E
  • super().__init__G.__init__;
  • G
  • super().__init__H.__init__;
  • H
  • super().__init__F.__init__;和
  • F
  • super().__init__object.__init__

所以这里 Dsuper() 导致 E(对于 __init__A.

的上下文中不一样
super().__init__(*args, **kwargs)   

发现您没有通过 "self" - 它是自动插入的。

super() 最初是在 Python 2 中设计的,以允许 classes 在 class 层次结构中以其直接上级 class 可能会改变:

让我们假设在某个时间点您的代码如下:

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__:但是当 superclass 名称被硬编码时,不会发生 - A__init__ 总是下一个。 如果您在 C 中硬编码 B.__init__,您将阻止 C 在没有 B 的情况下工作,从而破坏多重继承的目的。

当您改用 super() 时,Python 执行方法搜索下一个父级 class,寻找 class 的 __mro__属性(mro = 方法解析顺序。__mro__ 是附加到每个 Python class 的具体属性)。 - 因此,如果在某个时间点 class D 不再继承自 BC 中对 super().__init__ 的调用将自动重新路由直接到 A.

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