“超级”在Python中做什么?

有什么区别:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

和:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

我已经看到只有单一继承才能在类中使用super类。 我可以看到为什么你会在多重继承中使用它,但我不清楚在这种情况下使用它的优点。


super()在单一继承中的好处是最小的 - 大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中。

但是,如果没有super() ,使用多重继承几乎是不可能的。 这包括像mixin,接口,抽象类等常见的习惯用法。这扩展到后来扩展你的代码。 如果有人后来想写一个扩展Child和mixin的类,他们的代码将无法正常工作。


有什么不同?

SomeBaseClass.__init__(self) 

意味着调用SomeBaseClass__init__ 。 而

super(Child, self).__init__()

意味着从实例的方法解析顺序(MRO)中的Child __init__的父类调用绑定的__init__

如果实例是Child的子类,那么MRO中可能会有另一个父类。

Python 2与3

这适用于Python 2和3:

super(Child, self).__init__()

这只适用于Python 3:

super().__init__()

它通过在堆栈框架中移动并获取方法的第一个参数(通常为实例方法的self或类方法的cls - 但可以是其他名称)以及找到类中的类(例如Child )自由变量(它在名称__class__作为方法中的自由闭包变量)。

我更喜欢演示使用super的交叉兼容方式,但如果您只使用Python 3,则可以不带参数地调用它。

具有向前兼容性的间接方向

它给你什么? 对于单一继承,从静态分析的角度来看,这个问题的例子实际上是相同的。 但是,使用super会为您提供一个具有向前兼容性的间接层。

向前兼容对于经验丰富的开发人员非常重要。 你希望你的代码在你改变它的时候保持最小的变化。 当你看看你的修订历史时,你想看看什么时候改变。

你可以从单一的继承开始,但是如果你决定添加另一个基类,你只需要改变这个基础 - 如果基类在你继承的类中改变(比如添加了一个mixin),你就会改变这课没有什么。 特别是在Python 2中,将参数设置为super以及正确的方法参数是很困难的。 如果你知道你正在使用super单继承,这使得调试更加困难。

依赖注入

其他人可以使用您的代码并将父项注入方法解析中:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

假设你为你的对象添加了另一个类,并且希望在Foo和Bar之间插入一个类(用于测试或其他原因):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

使用un-super子程序无法注入依赖项,因为您正在使用的子项对要调用的方法进行了硬编码:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

但是,具有使用super的孩子的类可以正确地注入依赖项:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

处理评论

为什么在这个世界上这会有用?

Python通过C3线性化算法对复杂的继承树进行线性化,以创建方法解析顺序(MRO)。

我们希望按照该顺序查找方法。

对于在父级中定义的方法,在没有super的情况下按照该顺序查找下一个方法,则必须这样做

  • 从实例类型中获取mro
  • 寻找定义方法的类型
  • 用该方法找到下一个类型
  • 绑定该方法并用期望的参数调用它
  • UnsuperChild不应该访问InjectMe 。 为什么不是“总是避免使用super ”的结论? 我在这里错过了什么?

    UnsuperChild无法访问InjectMe 。 这是UnsuperInjector可以访问InjectMe -但不能调用类的方法,从它继承的方法UnsuperChild

    两个Child类都打算按照MRO中下一个相同的名称来调用一个方法,这可能是另一个它在创建时不知道的类。

    没有super硬件代码的父代的方法 - 因此限制了其方法的行为,并且子类不能在调用链中注入功能。

    super有更大的灵活性。 这些方法的调用链可以被拦截并注入功能。

    您可能不需要该功能,但可能需要代码的子代码。

    结论

    总是使用super引用父类,而不是对其进行硬编码。

    你打算引用下一个在线的父类,而不是专门看到孩子继承的父类。

    不使用super可以给你的代码的用户带来不必要的限制。


    这并不是所有这些都假设基类是一种新式类?

    class A:
        def __init__(self):
            print("A.__init__()")
    
    class B(A):
        def __init__(self):
            print("B.__init__()")
            super(B, self).__init__()
    

    在Python 2中不起作用。 class A必须是新样式的,即: class A(object)

    链接地址: http://www.djcxy.com/p/74021.html

    上一篇: What does 'super' do in Python?

    下一篇: Does Python have a string 'contains' substring method?