【问题标题】:What's the difference between super() and Parent class name?super() 和 Parent 类名有什么区别?
【发布时间】:2017-07-13 19:28:45
【问题描述】:

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

【问题讨论】:

  • super() 使用 MRO... 解决呼叫
  • 只有当您的子类有多个超类,或者您可能在某个时候更改您的子类以从不同的超类继承时才会有所不同。顺便说一句,“父类”不是继承的常用术语。
  • @khelwood:严格来说这不是真的:如果你继承了一个具有多重继承的类,你的super()实际上可以委托给一个slibing,而不是一个父类。它只是返回__mro__ 的下一行。

标签: python inheritance initialization super method-resolution-order


【解决方案1】:

这种情况中没有。但是一般而言,尤其是当您使用多重继承时,super() 会委托给 方法解析顺序 (MRO) 中的下一个对象 em>documentation 中指定:

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

返回一个代理对象,将方法调用委托给父级或 类型的兄弟类。这对于访问继承的方法很有用 已在类中被覆盖。搜索顺序同上 getattr() 使用,除了类型本身被跳过。

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

(...)

(复制,加粗)

比如说你定义了类似的类(借用自this question, where the MRO is discussed in more detail):

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

  • super().__init__AD.__init__
  • D 中的super().__init__B.__init__
  • super().__init__BC.__init__
  • super().__init__CE.__init__;
  • super().__init__EG.__init__
  • super().__init__GH.__init__
  • Hsuper().__init__F.__init__;和
  • super().__init__Fobject.__init__

因此请注意,super()本身并不代表父母。例如Dsuper()BB 不是D 的超类,所以它真的取决于对象的类型(不取决于类) .

现在对于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的上下文中认为:

  • D 中的super().__init__E.__init__
  • super().__init__EG.__init__
  • super().__init__GH.__init__
  • super().__init__HF.__init__;和
  • super().__init__Fobject.__init__

所以这里Dsuper() 导致E(对于__init__A 的上下文中不一样 .

【讨论】:

  • 我认为你错过了最重要的部分,因为你过早地结束了最后一句话:A 中的super() 将委托给DD 中的super() 调用将委托到B 而如果你从D 实例开始,它的super() 调用将转到E
  • 是的,您的编辑与我的评论交叉。好多了,但我仍然认为展示直接 D 实例 super() 去不同的地方会很有用。
  • @Duncan:这确实是个好主意。更新了。
【解决方案2】:
super().__init__(*args, **kwargs)   

认为你没有传递“自我”——它是自动插入的。

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

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

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__ 总是会紧随其后。 如果你在C 中硬编码B.__init__,你会阻止C 在没有B 的情况下工作,从而违背了多重继承的目的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-26
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    相关资源
    最近更新 更多