Python超级方法和调用的替代品

我看到无处不在的例子超类方法应该被调用:

super(SuperClass, instance).method(args) 

这样做有什么缺点:

 SuperClass.method(instance, args) 

考虑以下情况:

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

所以这些类形成了所谓的inheritance钻石:

  A / \ BC \ / D 

运行代码产量

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

这很糟糕,因为C__init__被跳过了。 原因是因为B__init__直接调用A__init__

super的目的是解决inheritance钻石 。 如果你没有评论

 # 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__()和调用A.__init__(self) ,但是你会错的。 在上述情况下, super(B,self).__init__()实际上调用C.__init__(self)

B抽烟, BC一无所知,但是super(B,self)知道叫C__init__ ? 原因是因为self.__class__.mro()包含C 换句话说, self (或在上面, foo )知道C

所以要小心 – 这两个是不可替代的。 他们可以产生截然不同的结果。

使用super 有陷阱。 在inheritance图中的所有类之间需要相当程度的协调。 (例如,它们必须与__init__具有相同的调用签名,因为任何特定的__init__都不知道其他__init__ super可能接下来调用哪一个,否则使用**kwargs 。)此外,您必须在所有地方使用super一致性。 跳过一次(如上面的例子),你击败super的全部目的。 看到更多的陷阱链接。

如果你完全控制了你的类层次结构,或者你避免了inheritance菱形,那么就不需要super

尽pipe你的例子有些误导,但是没有惩罚。 在第一个例子中,它应该是

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

这导致我引用Python文档 :

super有两种典型的用例。 在具有单一inheritance的类层次结构中, super可以用于引用父类,而不用明确地命名它们,从而使代码更易于维护。 这种用法与其他编程语言中的super用法非常类似。

第二个用例是支持dynamic执行环境中的协作多重inheritance。 这个用例是Python独有的,在静态编译的语言或仅支持单一inheritance的语言中没有find。 这使得在多个基类实现相同方法的情况下实现“菱形图”成为可能。 良好的devise规定,这种方法在每种情况下都具有相同的调用签名(因为调用的顺序是在运行时确定的,因为该顺序适应类层次结构中的更改,并且因为该顺序可以包含在运行时之前未知的同级类)。

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