有什么区别:
1 2 3 | class Child(SomeBaseClass): def __init__(self): super(Child, self).__init__() |
和:
1 2 3 | class Child(SomeBaseClass): def __init__(self): SomeBaseClass.__init__(self) |
我已经看到
在单继承中,
然而,如果没有
有什么区别呢?
1 | SomeBaseClass.__init__(self) |
表示调用
1 | super(Child, self).__init__() |
表示从在实例的方法解析顺序(MRO)中遵循
如果实例是子类的子类,则MRO中可能会出现另一个父类。
解释简单的
当您编写一个类时,您希望其他类能够使用它。
正如Bob Martin所说,好的架构允许您尽可能地推迟决策。
当另一个类子类化您编写的类时,它也可以从其他类继承。这些类可以有一个
如果没有
如果您正在编写供个人使用的代码,那么您可能并不关心这种区别。但是如果您希望其他人使用您的代码,使用
这在python2和python3中工作:
1 | super(Child, self).__init__() |
这只适用于python3:
1 | super().__init__() |
它工作不带参数通过移动堆栈帧中,第一个参数方法(通常为一个实例方法
我更喜欢演示使用
它给了你什么?对于单继承,从静态分析的角度来看,问题中的示例实际上是相同的。但是,使用
对于经验丰富的开发人员来说,向前兼容性非常重要。您希望您的代码在更改时保持最小的更改。当你查看你的复习历史时,你想准确地看到什么在什么时候发生了变化。
您可以从单个继承开始,但是如果您决定添加另一个基类,您只需要使用基来更改行—如果您继承的类中的基发生更改(例如添加了mixin),那么您不会在这个类中更改任何内容。特别是在python2中,将参数设置为
依赖注入
其他人可以使用你的代码并将父类注入方法解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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之间注入一个类(为了测试或者其他原因):
1 2 3 4 5 6 7 8 | 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 |
使用非超子元素不能注入依赖项,因为你使用的子元素已经硬编码了方法,要在它自己的方法之后调用:
1 2 3 | >>> o = UnsuperInjector() UnsuperChild.__init__(self) called SomeBaseClass.__init__(self) called |
但是,使用
1 2 3 4 | >>> o2 = SuperInjector() SuperChild.__init__(self) called InjectMe.__init__(self) called SomeBaseClass.__init__(self) called |
解决评论
Why in the world would this be useful?
Python通过C3线性化算法对一个复杂的继承树进行线性化,以创建一个方法分辨率顺序(MRO)。
我们希望方法按这个顺序查找。
对于在父类中定义的方法,如果要按照没有
The
UnsuperChild should not have access toInjectMe . Why isn't the conclusion"Always avoid usingsuper "? What am I missing here?
这两个子类都打算使用MRO中随后出现的相同名称来调用方法,这可能是在创建时没有意识到的另一个类。
没有
带有
您可能不需要该功能,但是代码的子类可能需要。
结论始终使用
您想要引用的是下一个in-line的父类,而不是您看到的子类继承自的父类。
不使用
所有这些不都假设基类是一个新样式的类吗?
1 2 3 4 5 6 7 8 | class A: def __init__(self): print("A.__init__()") class B(A): def __init__(self): print("B.__init__()") super(B, self).__init__() |
在python2中不能工作。
我用
例如,我们有下一个层次结构:
1 2 3 4 5 | A / \ B C \ / D |
在这种情况下,MRO (D)将是(仅适用于python3):
1 2 | In [26]: D.__mro__ Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object) |
让我们创建一个类,其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | In [23]: class A(object): # or with Python 3 can define class A: ...: def __init__(self): ...: print("I'm from A") ...: ...: class B(A): ...: def __init__(self): ...: print("I'm from B") ...: super().__init__() ...: ...: class C(A): ...: def __init__(self): ...: print("I'm from C") ...: super().__init__() ...: ...: class D(B, C): ...: def __init__(self): ...: print("I'm from D") ...: super().__init__() ...: d = D() ...: I'm from D I'm from B I'm from C I'm from A A / ? B ? C ? / D |
所以我们可以看到分辨率的顺序和MRO是一样的。但是当我们在方法的开头调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | In [21]: class A(object): # or class A: ...: def __init__(self): ...: print("I'm from A") ...: ...: class B(A): ...: def __init__(self): ...: super().__init__() # or super(B, self).__init_() ...: print("I'm from B") ...: ...: class C(A): ...: def __init__(self): ...: super().__init__() ...: print("I'm from C") ...: ...: class D(B, C): ...: def __init__(self): ...: super().__init__() ...: print("I'm from D") ...: d = D() ...: I'm from A I'm from C I'm from B I'm from D |
我们有一个不同的顺序它颠倒了MRO元组的顺序。
1 2 3 4 5 | A / ? B ? C ? / D |
对于补充阅读,我推荐以下答案:
C3超线性化例子(大层次结构)新旧样式类之间的重要行为更改关于新型类的内部故事当调用
考虑一个类层次结构
1 2 3 4 5 6 7 8 9 10 11 | super(B, b) # resolves to the scope of B's parent i.e. A # and applies that scope to b, as if b was an instance of A super(C, c) # resolves to the scope of C's parent i.e. B # and applies that scope to c super(B, c) # resolves to the scope of B's parent i.e. A # and applies that scope to c |
使用
例如,在
1 2 3 4 5 6 7 | class A(object): def __new__(cls, *a, **kw): # ... # whatever you want to specialize or override here # ... return super(A, cls).__new__(cls, *a, **kw) |
解释:
1-尽管
1 2 3 4 5 6 7 8 9 10 | # if you defined this class A(object): def __new__(cls): pass # calling this would raise a TypeError due to the missing argument A.__new__() # whereas this would be fine A.__new__(A) |
2-当调用
1 | super(A, cls) |
3-由于一般规则
1 | super(A, cls).__new__(cls, *a, **kw) |
4-在没有
1 2 3 4 5 6 7 | class A(object): def __new__(cls, *a, **kw): # ... # whatever you want to specialize or override here # ... return object.__new__(cls, *a, **kw) |
使用
例如,从
1 2 3 4 5 6 7 | class A(object): def __init__(self, *a, **kw): # ... # you make some changes here # ... super(A, self).__init__(*a, **kw) |
解释:
1-
1 2 3 4 5 6 7 8 9 10 11 12 | # you try calling `__init__()` from the class without specifying an instance # and a TypeError is raised due to the expected but missing reference A.__init__() # TypeError ... # you create an instance a = A() # you call `__init__()` from that instance and it works a.__init__() # you can also call `__init__()` with the class and explicitly pass the instance A.__init__(a) |
2-当在
1 | super(A, self) |
3-调用
4-要在没有
1 2 3 4 5 6 7 | class A(object): def __init__(self, *a, **kw): # ... # you make some changes here # ... object.__init__(self, *a, **kw) |
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class A(object): @classmethod def alternate_constructor(cls, *a, **kw): print"A.alternate_constructor called" return cls(*a, **kw) class B(A): @classmethod def alternate_constructor(cls, *a, **kw): # ... # whatever you want to specialize or override here # ... print"B.alternate_constructor called" return super(B, cls).alternate_constructor(*a, **kw) |
解释:
类方法可以直接从类中调用,并将类的引用作为它的第一个参数。
1 2 3 4 | # calling directly from the class is fine, # a reference to the class is passed implicitly a = A.alternate_constructor() b = B.alternate_constructor() |
2 -当调用
1 | super(B, cls_or_subcls) |
3-调用
1 | super(B, cls).alternate_constructor() |
4-在不使用
1 2 3 4 5 6 7 8 9 | class B(A): @classmethod def alternate_constructor(cls, *a, **kw): # ... # whatever you want to specialize or override here # ... print"B.alternate_constructor called" return A.alternate_constructor(cls, *a, **kw) |
上述方法不起作用,因为
1 2 3 4 5 6 7 8 9 10 11 12 13 | class B(A): @classmethod def alternate_constructor(cls, *a, **kw): # ... # whatever you want to specialize or override here # ... print"B.alternate_constructor called" # first we get a reference to the unbound # `A.alternate_constructor` function unbound_func = A.alternate_constructor.im_func # now we call it and pass our own `cls` as its first argument return unbound_func(cls, *a, **kw) |
1 2 3 | class Child(SomeBaseClass): def __init__(self): SomeBaseClass.__init__(self) |
这很容易理解。
1 2 3 | class Child(SomeBaseClass): def __init__(self): super(Child, self).__init__() |
好的,如果使用
创建子实例时,其MRO(方法解析顺序)的顺序是基于继承的(子、SomeBaseClass、对象)。(假设某个基类除了默认对象之外没有其他父类)
通过传递
对于多继承,MRO可以包含很多类,所以基本上