Python - 这是调用超类'方法的更好方法吗?

时间:2010-11-29 03:56:07

标签: python oop class

我一直在使用:

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

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

但是,我遇到的其他代码

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

代替。

当它用于:

时,它可能会变得暧昧
class MyClass(SuperClass1, SuperClass2):
    def __init__(self, *args, **kwargs):
        super(MyClass, self).__init__(*args, **kwargs) #which SuperClass is being used?

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

3 个答案:

答案 0 :(得分: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为每个被调用的子类解析为一个不同的类。这允许每个重写方法改变正在发生的一小部分,并且仍让其他每个超类对方法调用做出贡献只要他们愿意发挥得很好。

答案 1 :(得分:6)

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

类型__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__,因为First在MRO中排在第一位。由于在这种情况下“First”没有__init__,因此调用Second。如果__init__中有First,则只会调用{{1}}。

答案 2 :(得分:3)

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

  • 旧式类(不从对象派生的类)具有深度优先的方法解析顺序。旧式课程以你习惯的方式调用他们的超级课程。

  • 新式类(从对象派生的类)具有更复杂的方法解析顺序。在最高级别,它首先类似于广度,但比这更复杂。请参阅此page以获得出色的解释。使用“super()”允许新样式类遵循此方法解析顺序。

  • 如果您正在使用新式类,您仍然可以使用旧式调用超类,您的代码仍然有效。只有更复杂的多继承模式才会出现差异。