super()函数¶
super()函数的原理¶
super()的工作原理如下:
super(cls, instance):
mro = instance.__class__.mro()
return mro[mro.index(cls) + 1]
其中, cls代表类, instance代表实例, 上面的代码做了两件事:
- 获取
instance的MRO列表;- 查找
cls在当前MRO列表中的index, 并返回它的下一个类, 即mro[index + 1].
当使用super(cls, instance)时, Python会在instance的MRO列表中查找cls的下一个类.
使用super()函数调用超类的方法¶
使用super()函数, 可以调用子类的超类方法, 使用时, 将当前的类和对象分别作为参数传入.
在Python 3.x中, 在类中调用super()函数时, 可以不提供任何参数(隐式传入当前的类和self参数)来调用其超类方法.
尤其是在重写子类的构造函数时, 可以使用super()函数调用超类的构造函数.
但是, 在使用super()函数调用超类的构造函数, 尤其是在多重继承时, 需要特别注意一些问题.
- 不要混用super()函数与显式类调用
在子类的构造函数中调用超类的构造函数有两种方法: super()函数和显式的类调用.
这两种方法都可以, 但是不要混用它们.
注解
有时这种层次结构的一部分位于第三方代码中, 我们无法确定外部包的这些代码中是否使用了super(), 因此, 当需要对某个第三方类进行子类化时, 最好查看其内部代码以及MRO中其它类的内部代码.
Example:
class A():
def __init__(self):
print('A', end=' ')
super().__init__()
class B():
def __init__(self):
print('B', end=' ')
super().__init__()
class C(A, B):
def __init__(self):
print('C', end=' ')
A.__init__(self)
B.__init__(self)
print('MRO: ', [x.__name__ for x in C.__mro__])
C()
运行结果为:
MRO: ['C', 'A', 'B', 'object']
C A B B
可以看到, 上面的代码中, B的构造函数被执行了两次.
在C的构造函数中以显式调用的方式调用了A和B的构造函数, 在A和B的构造函数中使用了super()函数调用其父类的构造函数.
结合MRO列表和super()函数的原理, 在A的构造函数中调用super().__init__()(super(A, self).__init__()), 会使其调用B.__init__()方法, 所以导致B的构造函数被调用了两次.
根本原因还是混用了super()函数了显式函数调用.
- 如果类的层次结构中都是使用的
super()函数, 那么在子类的构造函数中只要调用一个super().__init__(), 就可以完成所有直接和间接父类的初始化.
Example:
class Base:
def __init__(self):
print('Base', end=' ')
class A(Base):
def __init__(self):
print('A', end=' ')
super().__init__()
class B(Base):
def __init__(self):
print('B', end=' ')
super().__init__()
class C(A, B):
def __init__(self):
print('C', end=' ')
super().__init__()
print('MRO: ', [x.__name__ for x in C.__mro__])
C()
运行结果为:
MRO: ['C', 'A', 'B', 'Base', 'object']
C A B Base
总结¶
- 尽量避免使用多重继承;
super()的使用必须一致, 即在类的层次结构中, 要么全部使用super(), 要么全不用, 混用super()和传统调用是一种混乱的写法;- 如果代码要兼容Python 2.x, 在Python 3.x中应该显式继承自
object, 在Python 2.x中没有指定任何祖先的类都被视为旧式类; - 调用父类时应该提前查看类的层次结构, 也就是使用类的
__mro__属性或者mro()方法查看有关类的MRO.