Python中super的应用
- 约定
- 单继承
- 多继承
- super 是个类
- 多继承中 super 的工作方式
- 参考资料
约定
在开始之前我们来约定一下本文所使用的 Python 版本。默认用的是 Python 3,也就是说:本文所定义的类都是新式类。如果你用到是 Python 2 的话,记得继承 object:
- # 默认, Python 3
- class A:
- pass
- # Python 2
- class A(object):
- pass
Python 3 和 Python 2 的另一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
- # 默认,Python 3
- class B(A):
- def add(self, x):
- super().add(x)
- # Python 2
- class B(A):
- def add(self, x):
- super(B, self).add(x)
所以,你如果用的是 Python 2 的话,记得将本文的 super() 替换为 suepr(Class, self) 。
如果还有其他不兼容 Python 2 的情况,我会在文中注明的。
单继承
在单继承中 super 就像大家所想的那样,主要是用来调用父类的方法的。
- class A:
- def __init__(self):
- self.n = 2
- def add(self, m):
- print('self is {0} @A.add'.format(self))
- self.n += m
- class B(A):
- def __init__(self):
- self.n = 3
- def add(self, m):
- print('self is {0} @B.add'.format(self))
- super().add(m)
- self.n += 3
你觉得执行下面代码后, b.n 的值是多少呢?
- b = B()
- b.add(2)
- print(b.n)
执行结果如下:
- self is <__main__.B object at 0x106c49b38> @B.add
- self is <__main__.B object at 0x106c49b38> @A.add
- 8
这个结果说明了两个问题:
- super().add(m) 确实调用了父类 A 的 add 方法。
- super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以b.add(2) 之后的结果是 5 而不是 4 。
不知道这个结果是否和你想到一样呢?下面我们来看一个多继承的例子。
多继承
这次我们再定义一个 class C,一个 class D:
- class C(A):
- def __init__(self):
- self.n = 4
- def add(self, m):
- print('self is {0} @C.add'.format(self))
- super().add(m)
- self.n += 4
- class D(B, C):
- def __init__(self):
- self.n = 5
- def add(self, m):
- print('self is {0} @D.add'.format(self))
- super().add(m)
- self.n += 5
下面的代码又输出啥呢?
- d = D()
- d.add(2)
- print(d.n)
这次的输出如下:
- self is <__main__.D object at 0x10ce10e48> @D.add
- self is <__main__.D object at 0x10ce10e48> @B.add
- self is <__main__.D object at 0x10ce10e48> @C.add
- self is <__main__.D object at 0x10ce10e48> @A.add
- 19
你说对了吗?你可能会认为上面代码的输出类似:
- self is <__main__.D object at 0x10ce10e48> @D.add
- self is <__main__.D object at 0x10ce10e48> @B.add
- self is <__main__.D object at 0x10ce10e48> @A.add
- 15
为什么会跟预期的不一样呢?下面我们将一起来看看 super 的奥秘。
super 是个类
当我们调用 super() 的时候,实际上是实例化了一个 super 类。你没看错, super 是个类,既不是关键字也不是函数等其他数据结构:
- >>> class A: pass
- ...
- >>> s = super(A)
- >>> type(s)
- <class 'super'>
- >>>
在大多数情况下, super 包含了两个非常重要的信息: 一个 MRO 以及 MRO 中的一个类。当以如下方式调用 super 时:
- super(a_type, obj)
MRO 指的是 type(obj) 的 MRO, MRO 中的那个类就是 a_type , 同时 isinstance(obj, a_type) == True 。
当这样调用时:
- super(type1, type2)
MRO 指的是 type2 的 MRO, MRO 中的那个类就是 type1 ,同时 issubclass(type2, type1) == True 。
那么, super() 实际上做了啥呢?简单来说就是:提供一个 MRO 以及一个 MRO 中的类 C , super() 将返回一个从 MRO 中 C 之后的类中查找方法的对象。
也就是说,查找方式时不是像常规方法一样从所有的 MRO 类中查找,而是从 MRO 的 tail 中查找。
举个栗子, 有个 MRO:
- [A, B, C, D, E, object]
下面的调用:
- super(C, A).foo()
super 只会从 C 之后查找,即: 只会在 D 或 E 或 object 中查找 foo 方法。
多继承中 super 的工作方式
再回到前面的
- d = D()
- d.add(2)
- print(d.n)
现在你可能已经有点眉目,为什么输出会是
- self is <__main__.D object at 0x10ce10e48> @D.add
- self is <__main__.D object at 0x10ce10e48> @B.add
- self is <__main__.D object at 0x10ce10e48> @C.add
- self is <__main__.D object at 0x10ce10e48> @A.add
- 19
了吧 ;)
下面我们来具体分析一下:
- D 的 MRO 是: [D, B, C, A, object] 。 备注: 可以通过 D.mro() (Python 2 使用 D.__mro__ ) 来查看 D 的 MRO 信息)
- 详细的代码分析如下:
- class A:
- def __init__(self):
- self.n = 2
- def add(self, m):
- # 第四步
- # 来自 D.add 中的 super
- # self == d, self.n == d.n == 5
- print('self is {0} @A.add'.format(self))
- self.n += m
- # d.n == 7
- class B(A):
- def __init__(self):
- self.n = 3
- def add(self, m):
- # 第二步
- # 来自 D.add 中的 super
- # self == d, self.n == d.n == 5
- print('self is {0} @B.add'.format(self))
- # 等价于 suepr(B, self).add(m)
- # self 的 MRO 是 [D, B, C, A, object]
- # 从 B 之后的 [C, A, object] 中查找 add 方法
- super().add(m)
- # 第六步
- # d.n = 11
- self.n += 3
- # d.n = 14
- class C(A):
- def __init__(self):
- self.n = 4
- def add(self, m):
- # 第三步
- # 来自 B.add 中的 super
- # self == d, self.n == d.n == 5
- print('self is {0} @C.add'.format(self))
- # 等价于 suepr(C, self).add(m)
- # self 的 MRO 是 [D, B, C, A, object]
- # 从 C 之后的 [A, object] 中查找 add 方法
- super().add(m)
- # 第五步
- # d.n = 7
- self.n += 4
- # d.n = 11
- class D(B, C):
- def __init__(self):
- self.n = 5
- def add(self, m):
- # 第一步
- print('self is {0} @D.add'.format(self))
- # 等价于 super(D, self).add(m)
- # self 的 MRO 是 [D, B, C, A, object]
- # 从 D 之后的 [B, C, A, object] 中查找 add 方法
- super().add(m)
- # 第七步
- # d.n = 14
- self.n += 5
- # self.n = 19
- d = D()
- d.add(2)
- print(d.n)
调用过程图如下:
- D.mro() == [D, B, C, A, object]
- d = D()
- d.n == 5
- d.add(2)
- class D(B, C): class B(A): class C(A): class A:
- def add(self, m): def add(self, m): def add(self, m): def add(self, m):
- super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m
- self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--|
- (14+5=19) (11+3=14) (7+4=11) (5+2=7)
现在你知道为什么 d.add(2) 后 d.n 的值是 19 了吧 ;
Python中super的应用的更多相关文章
- python中super().__init__和类名.__init__的区别
super().__init__相对于类名.__init__,在单继承上用法基本无差 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次 多继承时 ...
- Python中super的用法【转载】
Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143 收藏 展开 转载自 Python面向对象中super用法与MRO ...
- Python中super函数的用法
之前看python文档的时候发现许多单继承类也用了super()来申明父类,那么这样做有何意义? 从python官网文档对于super的介绍来看,其作用为返回一个代理对象作为代表调用父类或亲类方法.( ...
- python中super()的一些用法
在看python高级编程这本书的时候,在讲到super的时候,产生了一些疑惑,super在python中的用法跟其他的语言有一些不一样的地方,在网上找了一些资料,发现基本上很少有文章能把我的疑惑讲明白 ...
- python中super的理解(转)
原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...
- Python中super()的用法
参考链接:https://www.cnblogs.com/shengulong/p/7892266.html super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是 ...
- Python中super详解
转至:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html 说到 super, 大家可能觉得很 ...
- python中super的使用方法
说白了,super的使用就是要子类要调用父类的方法,我们就用super,那你要有调用的规范,我们明白这个规范就可以了. 在python2和python3中,调用方法不同,注意就是了.Python3.x ...
- python中super的使用
转自:http://python.jobbole.com/86787/ super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能, ...
随机推荐
- R语言:R2OpenBUGS
R语言:R2OpenBUGS 用这个包调用BUGS model,分别用表格和图形概述inference和convergence,保存估计的结果 as.bugs.array 转换成bugs object ...
- vs配置SP++3.0
最近在研究信号处理的C语言算法,突然发现一个西安交大的师兄之前已经写出来了一个完整的库,同样是研究生三年,差别怎么这样大呢. 先从用轮子开始吧. 一.SP++3.0安装及测试 官网下载地址: http ...
- 用canvas绘制验证码
在通常的登录界面我们都可以看到验证码,验证码的作用是检测是不是人在操作,防止机器等非人操作,防止数据库被轻而易举的攻破. 验证码一般用PHP和java等后端语言编写: 但是在前端,用canva或者SV ...
- mysql 复制表数据,表结构的3种方法
什么时候我们会用到复制表?例如:我现在对一张表进行操作,但是怕误删数据,所以在同一个数据库中建一个表结构一样,表数据也一样的表,以作备份.如果用mysqldump比较麻烦,备份.MYD,.MYI这样的 ...
- 京东全链路压测军演系统(ForceBot)架构解密
摘要:全链路压测是应对电商大促容量规划最有效的手段,如何有效进行容量规划是其中的架构关键问题.京东在全链路压测方面做过多年尝试,本文转载京东商城基础平台技术专家文章,介绍其最新的自动化压测 Force ...
- CSS3实战-文字篇
text-shadow的大作用 多颜色阴影效果,用逗号分隔text-shaodow即可. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...
- Visual Leak Detector原理剖析
认识VLD VLD(Visual Leak Detector)是一款用于Visual C++的开源内存泄漏检测工具,我们只需要在被检测内存泄漏的工程代码里#include “vld.h”就可以开启内存 ...
- 【转】C# Graphics类详解
Brush 类 .NET Framework 4 定义用于填充图形形状(如矩形.椭圆.饼形.多边形和封闭路径)的内部的对象. 属于命名空间: System.Drawing 这是一个抽象基类,不能进行 ...
- 用threading和Queue模块实现多线程的端口扫描器
一.Queue模块基础 q = Queue.Queue() q.qsize() 返回队列的大小 q.empty() 如果队列为空,返回True,反之Fals ...
- NYOJ 123 士兵杀敌(四) (线段树)
题目链接 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个 ...