深入理解super
程序员文章站
2024-03-24 13:47:28
...
在前段时间使用类的继承时,为了在子类构造函数中调用父类的构造函数,我们使用到了super方法。
使用套路如下:
super不仅仅只用在构造函数中调用父类的构造函数,普通的方法中也可以使用。
深入理解super()
看了上面的使用,我们可能会觉得super的使用很简单,无非就是获取父类,并调用父类的方法。其实,在上面的情况中,super获取到的正好是父类,但在其他情况下就不一定了。super其实和父类没有实质性的联系。
让我们写一个稍微复杂的例子,涉及到多重继承。
class Base(object):
def __init__(self):
print "enter Base"
print "leave Base"
class A(Base):
def __init__(self):
print "enter A"
super(A, self).__init__()
print "leave A"
class B(Base):
def __init__(self):
print "enter B"
super(B, self).__init__()
print "leave B"
class C(A, B):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
其中,Base是父类,A、B都继承自Base,C继承自A、B,它们的关系是一个典型的菱形继承,如下:
Base
/ \
/ \
A B
\ /
\ /
C
使用情况如下:
如果你认为super代表【调用父类的方法】,那么如何解释enterA的下一句不是enter Base而是enter B。原因是,super和父类没有实质性的关联。
MRO列表
事实上,对于你定义的每一个类,Python会计算出一个方法解析顺序(Method Resolution Order, MRO)列表。它代表了类继承的顺序。我们可以使用下面方法获取一个类的MRO列表。
类名.mro()
MRO列表的顺序是怎么定的呢?总的来说,一个类的MRO列表就是合并所有父类的MRO列表,并遵循以下三条原则:
- 子类永远在父类的前面
- 如果有多个父类,会根据它们在列表中的顺序依次被检查。
- 如果对下一个类存在两个合法的选择,选择第一个父类(并没有理解说的是什么)
super原理
super的工作原理如下:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls)+1]
其中,cls代表类,inst代表的是实例,上面的代码做了两件事:
- 获取inst的MRO列表
- 查找cls在当前MRO列表中的index,并返回它的下一个类,即mro[index + 1]
所以说,在当前类中调用super返回的是MRO列表中的下一个类,并不一定是父类,也可能是同级类(多重继承的情况)
当你使用super(cls, inst)
时,Python会在inst的MRO列表上搜索cls的下一个类。
至此,我们就解决了之前先输出enter B后输出enter Base的问题。
只要理解了super的工作机制,我们才会对这样的输出感到疑惑。
小结
- 事实上,super和父类没有实质性关联。
-
super(cls, inst)
获得的是当前cls(类)在MRO列表中的下一个类。