python 多继承详解-乾颐堂
|
1
2
3
4
5
6
7
8
9
10
|
class A(object): # A must be new-style class def __init__(self): print "enter A" print "leave A" class B(C): # A --> C def __init__(self): print "enter B" super(B, self).__init__() print "leave B" |
在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象,然后“被转换”的类A对象调用自己的__init__函数。 有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如下
代码段4:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class A(object): def __init__(self): print "enter A" print "leave A" class B(object): def __init__(self): print "enter B" print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" B.__init__(self) C.__init__(self) print "leave E" class F(E, D): def __init__(self): print "enter F" E.__init__(self) D.__init__(self) print "leave F" |
f = F() ,结果如下:
enter F enter E enter B leave B enter C enter D enter A leave A leave D leave C leave E enter D enter A leave A leave D leave F
明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:
object
| \
| A
| / |
B C D
\ / |
E |
\ |
F
按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!
也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:
F E B C D A object
从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。 ???
我们把代码段4改写为:
代码段5:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class A(object): def __init__(self): print "enter A" super(A, self).__init__() # new print "leave A" class B(object): def __init__(self): print "enter B" super(B, self).__init__() # new print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" super(E, self).__init__() # change print "leave E" class F(E, D): def __init__(self): print "enter F" super(F, self).__init__() # change print "leave F" |
f = F(),执行结果:
enter F enter E enter B enter C enter D enter A leave A leave D leave C leave B leave E leave F 可见,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。
小结
1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象;
2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);
5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。
一些更深入的问题:各位可以看到,print F.__mro__时发现里面元素的顺序是 F E B C D A object,这就是F的基类查找顺序,至于为什么是这样的顺序,以及python内置的多继承顺序是怎么实现的,这涉及到mro顺序的实现,python 2.3以后的版本中是采用的一个叫做C3的算法。
www.qytang.com/
http://www.qytang.com/cn/list/29/
http://www.qytang.com/cn/list/28/428.htm
http://www.qytang.com/cn/list/28/426.htm
http://www.qytang.com/cn/list/28/425.htm
http://www.qytang.com/cn/list/28/424.htm
http://www.qytang.com/cn/list/28/423.htm
http://www.qytang.com/cn/list/28/422.htm
http://www.qytang.com/cn/list/28/421.htm
http://www.qytang.com/cn/list/28/420.htm
http://www.qytang.com/cn/list/28/417.htm
http://www.qytang.com/cn/list/28/416.htm
http://www.qytang.com/cn/list/28/407.htm
http://www.qytang.com/cn/list/28/403.htm
python 多继承详解-乾颐堂的更多相关文章
- python时间处理详解-乾颐堂
1.获取当前时间的两种方法: import datetime,time now = time.strftime("%Y-%m-%d %H:%M:%S") print now now ...
- Python 解析配置模块之ConfigParser详解-乾颐堂
1.基本的读取配置文件 -read(filename) 直接读取ini文件内容 -sections() 得到所有的section,并以列表的形式返回 -options(section) 得到该sect ...
- xargs在linux中的使用详解-乾颐堂
xargs在linux中是个很有用的命令,它经常和其他命令组合起来使用,非常的灵活. xargs是给命令传递参数的一个过滤器,也是组合多个命令的一个工具.它把一个数据流分割为一些足够小的块,以方便过滤 ...
- HTTP 499状态码 nginx下499错误详解-乾颐堂
日志记录中HTTP状态码出现499错误有多种情况,我遇到的一种情况是nginx反代到一个永远打不开的后端,就这样了,日志状态记录是499.发送字节数是0. 老是有用户反映网站系统时好时坏,因为线上的产 ...
- Linux ls命令详解-乾颐堂CCIE
ls命令用法举例: 例一:列出/home文件夹下的所有文件和目录的详细资料: 1 ls -l -R /home 命令参数之前要有一短横线“-”, 上面的命令也可以这样写: 1 ls -lR /ho ...
- linux sed命令详解-乾颐堂CCIE
简介 sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的 ...
- nginx内置变量详解-乾颐堂
nginx的配置文件中可以使用的内置变量以美元符$开始,也有人叫全局变量.其中,部分预定义的变量的值是可以改变的. $arg_PARAMETER 这个变量值为:GET请求中变量名PARAMETER参数 ...
- Python super继承详解
MRO(Method resolution order)是python用来解析方法调用顺序的,mro中记录了一个类的所有基类的类类型序列,super不是简单地调用基类的方法,而是按照MRO中的顺序来调 ...
- 开发中常遇到的Python陷阱和注意点-乾颐堂
最近使用Python的过程中遇到了一些坑,例如用datetime.datetime.now()这个可变对象作为函数的默认参数,模块循环依赖等等. 在此记录一下,方便以后查询和补充. 避免可变对象作为默 ...
随机推荐
- 安装ecb
mac emacs上安装ecb,通过elpa折腾得要死,死活无法使用. 解决办法:下载https://github.com/alexott/ecb,添加路径,(require 'ecb),直接ok.
- qt在动态库里面加载widget的例子
testDll和testExe项目 备注:windows下dll内不需要new QApplication, linux和mac下面需要在动态库里面new QApplication testdll.h ...
- ASP.NET Redis 开发 入门
ASP.NET Redis 开发 文件并发(日志处理)--队列--Redis+Log4Net Redis简介 Redis是一个开源的,使用C语言编写,面向“键/值”对类型数据的分布式NoSQL数据 ...
- Vue.js实现数据的双向数据流
众所周知,Vue.js一直使用的是单向数据流的,和angularJs的双向数据流相比,单向数据流更加容易控制.Vue.js允许父组件通过props属性传递数据到子组件.但是有些情况下我们需要在子组件里 ...
- Jquery学习小计
实时监听输入框值变化 首先创建Jquery.fn扩展 jQuery.fn.extend({ inputChange: function(callback){ if($.support.leadingW ...
- Win10 TensorFlow(gpu)安装详解
Win10 TensorFlow(gpu)安装详解 写在前面:TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着 ...
- Java-Runoob-面向对象:Java 封装
ylbtech-Java-Runoob-面向对象:Java 封装 1.返回顶部 1. Java 封装 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细 ...
- 项目中Map端数据处理不均匀性分析
Map任务的不均匀性 最近发现Map端数据越来越不均匀,而处理输入的数据,写到本地磁盘的数据量都差不多,我们随便拿出来两个attempt任务(当前map数量为64个),33和45,33的counter ...
- PoJ 1595 PrimeCuts
Prime Cuts Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 9339 Accepted: 3562 Descri ...
- Linux下sz下载文件超过4G办法
当下载文件超过4G时,sz提示能下载. 解决办法有以下几种: 1.scp 2.ftp 3.nc 4.icmp 5.文件切割 ---------------------------------- 以上4 ...