Python - 如何更好地调用父类方法?

17

一直以来我一直在使用:

SuperClass.__init__(self, *args, **kwargs)

我的理由是这样可以明确显示使用的超类,特别是在多重继承的情况下。

然而,我遇到的其他代码使用

super(MyClass, self).__init__(*args, **kwargs)

这种写法在某些情况下会变得模糊不清,例如:

instead.

class MyClass(SuperClass1, SuperClass2):
    def __init__(self, *args, **kwargs):
        super(MyClass, self).__init__(*args, **kwargs) #which SuperClass is being used?

我想知道为什么这种调用方式被广泛采用?有什么优势吗?

3个回答

21

super 更适用于现代(新式)类,原因是它允许协同多重继承。以下是一个例子。

>>> class Foo(object):
...     def display(self):
...         print "In Foo"
... 
>>> class Foo2(Foo):
...     def display(self):
...         print "In Foo2"
...         super(Foo2, self).display()
...         print "Back in Foo2"
... 
>>> class Bar(Foo):
...     def display(self):
...         print "In Bar"
...         super(Bar, self).display()
...         print "Back in Bar"
... 
>>> class FooBar(Foo2, Bar):
...     pass
... 
>>> FooBar().display()
In Foo2
In Bar
In Foo
Back in Bar
Back in Foo2
>>> class BarFoo(Bar, Foo2):
...     pass
... 
>>> BarFoo().display()
In Bar
In Foo2
In Foo
Back in Foo2
Back in Bar

请注意,我没有改变超类上的display方法,但通过改变超类的排列顺序,子类上的display方法是不同的。 BarFooFooBar有不同的方法。这是因为它们具有不同的方法解析顺序

>>> BarFoo.__mro__
(<class '__main__.BarFoo'>, <class '__main__.Bar'>, <class '__main__.Foo2'>, <class '__main__.Foo'>, <type 'object'>)
>>> FooBar.__mro__
(<class '__main__.FooBar'>, <class '__main__.Foo2'>, <class '__main__.Bar'>, <class '__main__.Foo'>, <type 'object'>)

这意味着super在每个子类中被调用时都会解析为一个不同的类。这使得每个重写方法可以改变正在进行的一小部分,只要它们愿意友好地协作,让每个父类对方法调用做出贡献。


6

对于继承自object的新样式类,使用super

type__mro__(方法解析顺序)属性列出了super使用的方法解析搜索顺序。

>>> class X(object):
    pass

>>> class Y(object):
    pass

>>> class Z(X, Y):
    pass

>>> Z.__mro__
(<class '__main__.Z'>, <class '__main__.X'>, <class '__main__.Y'>, <type 'object'>)

这指定了Z的排序方式。由于它是Z(X, Y),所以X在层次结构中排在第一位。如果是Z(Y, X),那么Y将会在X之前。

对于旧式类,使用SuperClass.__init__(self, *args, **kwargs)

更新:

对于您关于使用哪个SuperClass的问题。

>>> class First(object):
    pass

>>> class Second(object):
    def __init__(self, *args, **kwargs):
        print 'Second __init__ called'

>>> class MInherit(First, Second):
    def __init__(self):
        super(MInherit, self).__init__()

>>> i = MInherit()
Second __init__ called

首先,将检查First是否有__init__,因为在MRO中First排在第一位。由于这种情况下'First'没有__init__,因此调用了Second。如果First中有__init__,则只会调用它。


3

除了已经发布的好答案外,这里还有一些额外的信息:

  • 旧式类(那些不派生自object的类)具有深度优先的方法解析顺序。旧式类以您习惯的方式调用它们的超级类。

  • 新式类(那些派生自object的类)具有更复杂的方法解析顺序。在最高层面上,它类似于广度优先,但比广度优先更复杂。请参见此page以获得出色的解释。使用"super()"允许新式类遵循这种方法解析顺序。

  • 如果您正在使用新式类,仍然可以使用旧式调用超级类的方式,您的代码仍将正常工作。只有在更复杂的多重继承模式中才会显现出差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接