Python超级方法和调用替代方案

时间:2011-02-17 19:58:54

标签: python class super

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

super(SuperClass, instance).method(args)

做任何不利之处:

SuperClass.method(instance, args)

2 个答案:

答案 0 :(得分:101)

考虑以下情况:

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的{​​{1}}被跳过了。这是因为__init__的{​​{1}}直接调用B的{​​{1}}。

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

神圣的烟雾,A.__init__(self)super(B,self).__init__()一无所知,但C.__init__(self)知道打电话给B的{​​{1}}?原因是C包含super(B,self)。换句话说,C(或上面的__init__)知道self.__class__.mro()

所以要小心 - 两者都不可替代。他们可以产生截然不同的结果。

使用C has pitfalls.继承图中的所有类之间需要相当程度的协调。 (例如,它们必须具有self的相同呼叫签名,因为任何特定的foo都不知道下一个C super可能会调用哪个,或者 否则use **kwargs。)此外,您必须始终如一地使用__init__。略过一次(如上例所示),你就失去了__init__的全部目的。 请参阅链接了解更多陷阱。

如果您完全控制了您的类层次结构,或者您避免使用继承钻石,那么就不需要__init__

答案 1 :(得分:7)

虽然你的例子有点误导,但没有任何惩罚。在第一个例子中,它应该是

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

这引导我引用Python docs

  

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

     

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

基本上,通过使用第一种方法,您不必在那里为单类层次结构硬编码您的父类,并且您根本无法使用第二种方法真正做到您想要的(有效/有效)使用多重继承时。