【问题标题】:Super init vs. parent.__init__超级初始化与父级.__init__
【发布时间】:2015-05-24 06:17:13
【问题描述】:

一个 Python 子类可以在调用或不调用 super() 的情况下进行初始化,如下所示

class Parent(object):
    ...

class Child(Parent):

    def __init__(self):
        super(Child, self).__init__()


class Child(Parent):

    def __init__(self):
        Parent.__init__(self)

这些情况之间有什么区别,一种通常比另一种更可取?

【问题讨论】:

  • 最后一个给你TypeError: unbound method __init__() must be called with Parent instance as first argument (got nothing instead),正是因为缺少参数。
  • Parent.__init__(self) 我相信你的意思是我喜欢这种方法,因为它对我来说更明确...... MRO 是可怕的黑魔法
  • 是的,对不起,我想我的意思是让我改变它。
  • 谢谢大家,我编辑了标签和代码。
  • 创建一个inheritance diamond。然后超级调用将调用不同的init 方法而不是对Parent.__init__ 的调用。

标签: python python-2.7 inheritance


【解决方案1】:

super 的目的是处理继承钻石。如果上课 继承结构只使用单继承,然后使用 super() 将 导致与显式调用“父”类相同的调用。

考虑这个继承菱形:

class A(object):
    def __init__(self):
        print('Running A.__init__')
        super(A,self).__init__()

class B(A):
    def __init__(self):
        print('Running B.__init__')        
        super(B,self).__init__()

class C(A):
    def __init__(self):
        print('Running C.__init__')
        super(C,self).__init__()

class D(B,C):
    def __init__(self):
        print('Running D.__init__')
        super(D,self).__init__()

foo = D()

打印出来的

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__

如果我们将B 更改为B2 并使用显式调用父__init__

class B2(A):
    def __init__(self):
        print('Running B.__init__')        
        A.__init__(self) 

class D2(B2,C):
    def __init__(self):
        print('Running D.__init__')
        super(D2,self).__init__()

bar = D2()

那么 init 调用链就变成了

Running D.__init__
Running B.__init__
Running A.__init__

因此对C.__init__ 的调用被完全跳过。


没有一个首选的选项。

如果你能保证你不想支持多重继承,那么 显式父调用更简单、更清晰。

如果您希望现在或将来支持多重继承,那么您需要使用super()。但是要理解使用 super 时会涉及到 some pitfalls,但是使用 proper use 可以避免这些陷阱。

【讨论】:

  • 也许我遗漏了一些东西,但第二个示例实际上似乎并没有调用显式类 init。它只是调用 super,就像它上面的那个。
  • 第二个例子首先调用了 super,但是 B2 调用的是 A.__init__ 而不是 super(A,self).__init__(),这意味着,A 没有调用 C,因为使用了显式调用。
【解决方案2】:

super(Child, self).__init__() 的主要目的是允许初始化在具有菱形继承结构的多重继承的情况下正常运行。如果您显式调用具有多重继承的基类构造函数,则某些初始化程序可能会被调用两次。对于单继承,使用 super 和显式调用基类 __init__() 方法之间没有功能上的区别。请注意,因为所有python新式类都是子类对象,所以多重继承总是涉及菱形继承。

如果您重命名或更改基类,super 在减少需求更改方面的好处较小。

在 python 3 中,super 的参数是可选的,所以你可以直接使用super().__init__()。 Python 2 仍然要求您显式提供参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    相关资源
    最近更新 更多