“超级”在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
的情况下按照该顺序查找下一个方法,则必须这样做
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)