鱼C论坛

 找回密码
 立即注册
查看: 13121|回复: 74

[技术交流] Python细节之6、类的多继承中super函数的调用顺序

[复制链接]
发表于 2020-4-29 10:33:50 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

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__()。
举个例子,给出这样一个继承关系图,如下图所示:
微信截图_20200429103656.jpg

按照上图的继承关系,看看下面的程序,分析一下 F 类的 __mro__ 列表的顺序是什么,程序的执行过程又是怎样的呢?
  1. class A():
  2.     def __init__(self):
  3.         print("进入A…")
  4.         print("离开A…")

  5. class G():
  6.     def __init__(self):
  7.         print("进入G…")
  8.         super().__init__()
  9.         print("离开G…")
  10.         
  11. class B(A):
  12.     def __init__(self):
  13.         print("进入B…")
  14.         super().__init__()
  15.         print("离开B…")
  16.         
  17. class C(A):
  18.     def __init__(self):
  19.         print("进入C…")
  20.         super().__init__()
  21.         print("离开C…")

  22. class D(B, C):
  23.     def __init__(self):
  24.         print("进入D…")
  25.         print("离开D…")

  26. class E(G):
  27.     def __init__(self):
  28.         print("进入E…")
  29.         super().__init__()
  30.         print("离开E…")

  31. class F(E,D):
  32.     def __init__(self):
  33.         print("进入F…")
  34.         super().__init__()
  35.         print("离开F…")        

  36. print(F.__mro__)
  37. d = F()
复制代码


你可以先按照上面所说的,自己想一下运行的结果会是什么,然后再看下面的答案。
游客,如果您要查看本帖隐藏内容请回复

三、使用含参数的 super() 调用父类的初始化函数 __init__()

实际上,使用 super 的时候也可以不完全按照 __mro__() 列表执行。因为 super 有两个参数,第一个参数是父类名,第二个参数是实例化参数self,可以根据第一个参数跳跃去执行对应类的 __init__()


具体会怎么跳呢?我们还以刚刚的这个继承关系为例。修改一下各个类的初始化函数 __init__()。


  1. class A():
  2.     def __init__(self):
  3.         print("进入A…")
  4.         print("离开A…")

  5. class G():
  6.     def __init__(self):
  7.         print("进入G…")
  8.         print("离开G…")
  9.         
  10. class B(A):
  11.     def __init__(self):
  12.         print("进入B…")
  13.         super(C,self).__init__()
  14.         print("离开B…")
  15.         
  16. class C(A):
  17.     def __init__(self):
  18.         print("进入C…")
  19.         super(D,self).__init__()
  20.         print("离开C…")

  21. class D(B, C):
  22.     def __init__(self):
  23.         print("进入D…")
  24.         super().__init__()
  25.         #super(A,self).__init__()
  26.         print("离开D…")

  27. class E(G):
  28.     def __init__(self):
  29.         print("进入E…")
  30.         #super().__init__()
  31.         super(B,self).__init__()
  32.         print("离开E…")

  33. class F(E,D):
  34.     def __init__(self):
  35.         print("进入F…")
  36.         super().__init__()
  37.         #super(G,self).__init__()
  38.         print("离开F…")        
  39. print(F.__mro__)
  40. d = F()
复制代码

可以看到我们修改之后,有些类的 __init__() 函数中在调用 super 的时候加上了参数。例如 class E 中是 super(B,self).__init__()。这个是去执行 class B 的初始化函数吗? NO!!自己先思考一下,再看下面的结果,看看是否和你想象的一样

游客,如果您要查看本帖隐藏内容请回复

关于这种复杂继承的初始化问题,这里只是进行了方法上的研究,在我们平时学习的过程中应该很少遇到。在今后工作过程中一旦遇到这么复杂的继承关系,那么将会更加复杂,因为初始化还会涉及到数据成员的初始化赋值等很多问题。


以上就是我所理解的super相关的用法。若有不足之处,欢迎各位批评指正。


评分

参与人数 3荣誉 +5 鱼币 +9 贡献 +6 收起 理由
小甲鱼 + 2 + 3 + 3 真的非常棒!
zltzlt + 3 + 3 + 3 鱼C有你更精彩^_^
永恒的蓝色梦想 + 3

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-4-29 10:35:05 | 显示全部楼层
沙发
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-4-29 10:38:44 | 显示全部楼层

你可真快,我帖子还没编辑完呢
发帖之后,我还在看有没有错误呢,你也快帮我看看,有没有错别字之类的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 10:49:18 | 显示全部楼层
前排支持~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 10:50:25 | 显示全部楼层
__mro__ 不是个元组嘛
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 10:55:49 | 显示全部楼层

对滴。__mro__在python变量分类中是元组。
这里之所以称之为列表,是因为我们平常所说的都是顺序查表,没有说顺序查组的。
实际上元组就是一个有序列表(这里所谓的列表不是python的一个类型哟)
若严格来说的话,或许的确应该把帖子中的__mro__列表改为__mro__元组。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2020-4-29 10:58:33 | 显示全部楼层
为了免去乱七八糟的继承烦恼,我建议大家调用时直接用父类名调用
开个玩笑
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 11:00:31 | 显示全部楼层
永恒的蓝色梦想 发表于 2020-4-29 10:58
为了免去乱七八糟的继承烦恼,我建议大家调用时直接用父类名调用
开个玩笑

看帖子中黄底 的那一行字~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 11:02:58 | 显示全部楼层
sunrise085 发表于 2020-4-29 11:00
看帖子中黄底 的那一行字~~

emmm……什么时候会出现这种情况呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 0 反对 1

使用道具 举报

 楼主| 发表于 2020-4-29 11:13:05 | 显示全部楼层
永恒的蓝色梦想 发表于 2020-4-29 11:02
emmm……什么时候会出现这种情况呢?

听说过钻石继承吗?
就像这个图中说话的ABCD四个类的继承方式那样,是个菱形的,称之为钻石继承
若用父类名调用__init__,那么A的初始化__init__会被调用两次:D调用B调用A,调用C调用A
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 11:24:06 | 显示全部楼层
sunrise085 发表于 2020-4-29 11:13
听说过钻石继承吗?
就像这个图中说话的ABCD四个类的继承方式那样,是个菱形的,称之为钻石继承
若用父 ...

emmmm……然而 super() 也无法解决这个问题。
你说的这种情况只会在
  1. B.__init__(self)
  2. C.__init__(self)
复制代码
的时候出现

但是 super 成功一次立马停手,所以说 super 并不能解决这个问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 11:25:57 | 显示全部楼层
永恒的蓝色梦想 发表于 2020-4-29 11:24
emmmm……然而 super() 也无法解决这个问题。
你说的这种情况只会在的时候出现

super没有解决重复调用的问题?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 11:29:25 | 显示全部楼层
sunrise085 发表于 2020-4-29 11:25
super没有解决重复调用的问题?


我好像理解错了什么……抱歉搞错了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 11:31:31 | 显示全部楼层
永恒的蓝色梦想 发表于 2020-4-29 11:29
你手动重复调用 super 也没办法啊

。。。。。
你这就是抬杠了。。。。。
我也不再说什么了。。。。
。。。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 11:32:18 | 显示全部楼层
sunrise085 发表于 2020-4-29 11:31
。。。。。
你这就是抬杠了。。。。。
我也不再说什么了。。。。

抱歉搞错了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 14:14:04 | 显示全部楼层
@zltzlt 申精。第一次申精,按照申精帖调整了一下格式,但是不知道是否符合条件~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 17:07:05 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-29 17:13:04 | 显示全部楼层
sunrise085 发表于 2020-4-29 14:14
@zltzlt 申精。第一次申精,按照申精帖调整了一下格式,但是不知道是否符合条件~~

我没有权限加精华,最终还要管理员大大审核
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-29 17:16:15 | 显示全部楼层
zltzlt 发表于 2020-4-29 17:13
我没有权限加精华,最终还要管理员大大审核

哦哦,多谢帮忙@管理员。我还没加管理员好友呢,无法@
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-30 11:41:53 | 显示全部楼层
厉害了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-27 10:13

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表