58

我到处都看到应该通过以下方式调用超类方法的示例:

super(SuperClass, instance).method(args)

这样做有什么缺点:

SuperClass.method(instance, args)
4

2 回答 2

112

考虑以下情况:

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__()
        A.__init__(self) 

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

所以这些类形成了一个所谓的继承菱形:

    A
   / \
  B   C
   \ /
    D

运行代码产生

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

这很糟糕,因为C's__init__被跳过了。这样做的原因是因为B'直接__init__调用A' 。__init__

目的super是解决传承钻石。如果你取消评论

# super(B,self).__init__()

并注释掉

A.__init__(self) 

该代码产生了更理想的结果:

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

现在所有的__init__方法都被调用了。请注意,在您定义时,B.__init__您可能认为super(B,self).__init__()与 call 相同A.__init__(self),但您错了。在上述情况下,super(B,self).__init__()实际调用C.__init__(self).

圣烟, 一无所知BCsuper(B,self)知道打电话C__init__原因是因为self.__class__.mro()包含C. 换句话说,self(或在上面,foo)知道关于C

所以要小心——这两者是不可替代的。它们可以产生截然不同的结果。

使用super 有陷阱。在继承图中的所有类之间需要相当程度的协调。(例如,它们必须对 具有相同的调用签名__init__,因为任何特定__init__的人都不知道__init__ super接下来会调用哪个,或者使用**kwargs。)此外,您必须始终如一地在super任何地方使用。跳过它一次(如上面的例子),你就破坏了super. 查看链接了解更多陷阱。

如果您可以完全控制类层次结构,或者避免继承菱形,那么就不需要super.

于 2011-02-17T20:46:14.437 回答
8

尽管您的示例有些误导,但没有按原样进行处罚。在第一个例子中,它应该是

super(SubClass, instance).method(args)  # Sub, not SuperClass

这导致我引用Python 文档

有两个典型的用例super。在具有单继承的类层次结构中,super可用于引用父类而不显式命名它们,从而使代码更易于维护。这种使用与super其他编程语言中的使用非常相似。

第二个用例是在动态执行环境中支持协作多重继承。这个用例是 Python 独有的,在静态编译的语言或仅支持单继承的语言中找不到。这使得实现多个基类实现相同方法的“菱形图”成为可能。良好的设计要求此方法在每种情况下都具有相同的调用签名(因为调用的顺序是在运行时确定的,因为该顺序会适应类层次结构的变化,并且因为该顺序可以包括在运行前未知的兄弟类)。

基本上,通过使用第一种方法,您不必在其中为单类层次结构硬编码父类,并且在使用多个方法时,您根本无法真正(有效/有效地)使用第二种方法做您想做的事遗产。

于 2011-02-17T20:13:57.117 回答