马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 sunrise085 于 2020-4-29 14:09 编辑
Python细节之6、类的多继承中super函数的调用顺序
好久没有发过帖子了。
不说废话,直奔主题。
一、类的继承中,子类对象初始化
面向对象编程的过程中,不免会用到类的继承,尤其是多重继承,关于子类的初始化会有这么几种情况:
情况一:子类需要自动调用父类的方法:子类不重写__init__()方法,实例化子类后,会自动调用父类的 __init__() 的方法。
情况二:子类不需要自动调用父类的方法:子类重写__init__()方法,实例化子类后,将不会自动调用父类的 __init__() 的方法。
情况三:子类重写 __init__() 方法又需要调用父类的方法,这时候就需要显式地调用父类的 __init__() 方法。
调用父类的 __init__() 方法,有两种方式:一种是直接使用 父类名. __init__() ;一种是使用 super(). __init__()。
父类名. __init__() 这种方式,更加直接,更加定向地去调用对应的 __init__() 函数。但是可能会遇到重复调用的问题,甚至可能会进入死循环。
二、使用 super() 调用父类的初始化函数 __init__()
super(). __init__() 这种方式,则会按照一定的规则去调用相应的 __init__() 函数。
super(). __init__() 所遵循的规则是什么呢?我们需要先看一下 __mro__ 列表(有鱼油提出, __mro__ 是一个元组不是列表,的确在 Python 中它的类型是元组,不过这里是为了更加方便去理解 super 如何去查表执行的,你若感觉不应称之为列表,那就称之为元组吧)。__mro__列表是个什么鬼呢? 它是多重继承中,子类中继承父类的顺序,可以通过 子类. __mro__ 查看该列表。 当子类调用 __init__() 函数过程中,若是遇到 super(). __init__()则会按照 __mro__ 列表的顺序去调用相对应的父类的 __init__() 函数。在执行过程中,每遇到一次 super,就会按照 __mro__ 列表的顺序去调用下一个类的 __init__()。
举个例子,给出这样一个继承关系图,如下图所示:
按照上图的继承关系,看看下面的程序,分析一下 F 类的 __mro__ 列表的顺序是什么,程序的执行过程又是怎样的呢?class A():
def __init__(self):
print("进入A…")
print("离开A…")
class G():
def __init__(self):
print("进入G…")
super().__init__()
print("离开G…")
class B(A):
def __init__(self):
print("进入B…")
super().__init__()
print("离开B…")
class C(A):
def __init__(self):
print("进入C…")
super().__init__()
print("离开C…")
class D(B, C):
def __init__(self):
print("进入D…")
print("离开D…")
class E(G):
def __init__(self):
print("进入E…")
super().__init__()
print("离开E…")
class F(E,D):
def __init__(self):
print("进入F…")
super().__init__()
print("离开F…")
print(F.__mro__)
d = F()
你可以先按照上面所说的,自己想一下运行的结果会是什么,然后再看下面的答案。
三、使用含参数的 super() 调用父类的初始化函数 __init__()
实际上,使用 super 的时候也可以不完全按照 __mro__() 列表执行。因为 super 有两个参数,第一个参数是父类名,第二个参数是实例化参数self,可以根据第一个参数跳跃去执行对应类的 __init__()。
具体会怎么跳呢?我们还以刚刚的这个继承关系为例。修改一下各个类的初始化函数 __init__()。 class A():
def __init__(self):
print("进入A…")
print("离开A…")
class G():
def __init__(self):
print("进入G…")
print("离开G…")
class B(A):
def __init__(self):
print("进入B…")
super(C,self).__init__()
print("离开B…")
class C(A):
def __init__(self):
print("进入C…")
super(D,self).__init__()
print("离开C…")
class D(B, C):
def __init__(self):
print("进入D…")
super().__init__()
#super(A,self).__init__()
print("离开D…")
class E(G):
def __init__(self):
print("进入E…")
#super().__init__()
super(B,self).__init__()
print("离开E…")
class F(E,D):
def __init__(self):
print("进入F…")
super().__init__()
#super(G,self).__init__()
print("离开F…")
print(F.__mro__)
d = F()
可以看到我们修改之后,有些类的 __init__() 函数中在调用 super 的时候加上了参数。例如 class E 中是 super(B,self).__init__()。这个是去执行 class B 的初始化函数吗? NO!!自己先思考一下,再看下面的结果,看看是否和你想象的一样
关于这种复杂继承的初始化问题,这里只是进行了方法上的研究,在我们平时学习的过程中应该很少遇到。在今后工作过程中一旦遇到这么复杂的继承关系,那么将会更加复杂,因为初始化还会涉及到数据成员的初始化赋值等很多问题。
以上就是我所理解的super相关的用法。若有不足之处,欢迎各位批评指正。
|