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

Ian*_*nSR 56 python class super

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

super(SuperClass, instance).method(args)
Run Code Online (Sandbox Code Playgroud)

这样做有什么不利之处:

SuperClass.method(instance, args)
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 105

考虑以下情况:

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()
Run Code Online (Sandbox Code Playgroud)

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

    A
   / \
  B   C
   \ /
    D
Run Code Online (Sandbox Code Playgroud)

运行代码产生

Running D.__init__
Running B.__init__
Running A.__init__
Run Code Online (Sandbox Code Playgroud)

这是因为不好C__init__将被跳过.其原因是因为B__init__调用A__init__直接.

目的super是解决继承钻石问题.如果你取消评论

# super(B,self).__init__()
Run Code Online (Sandbox Code Playgroud)

和评论

A.__init__(self) 
Run Code Online (Sandbox Code Playgroud)

代码产生更可取的结果:

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__
Run Code Online (Sandbox Code Playgroud)

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

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

所以要小心 - 这两个是不可替代的.它们可以产生截然不同的结果.

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

如果你完全控制你的类层次结构,或者你避免继承钻石,那么就没有必要了super.

  • [Python的super()被认为是超级!](http://rhettinger.wordpress.com/2011/05/26/super-considered-super/)显示了为什么以及如何使用`super()`.顺便说一句,有了新式类和多重继承,总有一个继承钻石,因为常见的`object` parent. (3认同)
  • +1为超级有害链接.我本人即将发布. (2认同)

Dan*_*olo 8

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

super(SubClass, instance).method(args)  # Sub, not SuperClass
Run Code Online (Sandbox Code Playgroud)

这引导我引用Python文档:

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

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

基本上,通过使用第一种方法,您不必对单类层次结构中的父类进行硬编码,并且在使用多个层次结构时,您根本无法使用第二种方法(高效/有效)执行所需操作遗产.