python继承 super()
写这篇博文,始于以下问题的探究:
#coding:utf-8
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()
使用super()和通过指定父类方法混用
在上述代码中,类C、D使用super()函数,类E、F通过直接指定父类方法的方式。在此,继承关系用图表示如下:
预想的输出应该是:
enter F
enter E
enter B
leave B
enter C
enter A
leave A
leave C
leave E
enter D
enter A
leave A
leave D
leave 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
实际输出
又一次自以为是的想错了~~
查阅资料可知,super的调用顺序是使用C3算法得出的。下面讲下C3算法的规则:
C3算法的核心在merge列表,merge中存放mro(method resolution order)列表,经过某种规则得到最终的调用顺序列表,而初始调用顺序列表中只存放自身类。规则如下:
在merge列表中,如果第一个mro列表的第一个类是出现在其它mro列表中,并且也是第一个或者不出现其它mro列表,那么这个类就会从这些mro列表中删除,并添加到调用顺序列表中。
比如以下示例的调用顺序规则演示如下:
class A(O):pass
class B(O):pass
class C(O):pass
class D(A,B):pass
class E(C,D):pass
示例
mro(A) = [A, O]
mro(B) = [B, O]
mro(C) = [C, O]
mro(D) = [D] + merge(mro(A), mro(B), [A, B])
= [D] + merge([A, O], [B, O], [A, B])
= [D, A] + merge([O], [B, O], [B])
= [D, A, B] + merge([O], [O])
= [D, A, B, O]
mro(E) = [E] + merge(mro(C), mro(D), [C, D])
= [E] + merge([C, O], [D, A, B, O], [C, D])
= [E, C] + merge([O], [D, A, B, O], [D])
= [E, C, D] + merge([O], [A, B, O])
= [E, C, D, A, B] + merge([O], [O])
= [E, C, D, A, B, O]
一目了然了吧
,这样就可计算出调用顺序了,这个序列存储在MRO只读列表中。比如类E是多重继承,在E中使用super访问某个函数,访问的是哪个类呢?首先在E类继承类中查找,也就是调用顺序表中E后的类C,类C也没有的话就接着查找类D(
在这里记着都是查找类本身是否定义了该函数,而不要混淆继承来的函数
),就这样按照访问顺序列表一直往后查找,直到找到调用的函数。求解访问顺序的规则有没有更简便的方式呢?有的。理解这么一句话:C3算法是从左到右深度遍历一条路径到它和另一条路径的交叉点前,再深度遍历另一条路径最后遍历交叉点。在这个例子中多重继承的图解如下,根据图去理解就很容易得出C3的访问顺序规则
了
[E, C, D, A, B, O]
。
上面介绍了继承类中函数访问顺序规则,这样最开始的例子预想出错是因为super()和指定类方法调用的混用。考虑下如果都换为super()调用,是不是就对了呢?是的。实际上不只是super()调用顺序按照C3算法规则,其它普通函数调用也都是这样一层一层往下查找的。![]()
问题1:为什么不直接使用指定类方法来调用,而使用super()呢?问题2:为什么要采用C3算法的访问顺序呢?
如果多重继承类过多,那么使用指定类来调用方法显得多余繁琐,需要指明类并且假如某一子类的父类发生了变化,由C变成了D,需要遍历该子类,把所有的原先的父类C换为D,很麻烦。所以引入super()不用考虑指明它的上一层是哪个父类。
基于问题2,我们看如下两个例子,比较不同:
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass d = D()
d.foo1()
输出为:A
class A(object):
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass d = D()
d.foo1()
输出为:C
很容易看出,后者的基类是继承了object类。这样就引出了旧式类和新式类的差别,旧式类是在python2.2以前常用的,基类没有继承object类,在多重继承时调用函数会出现问题。它采用的访问顺序是从左到右的深度遍历,拿上面那个例子来说,D本身没有重写foo1(),则先看B中有没有foo1(),没有则看B继承的A类中有没有,有则调用,绕过了C类中重新的foo1()。
而新式类,基类都继承object类,在访问顺序上采用C3算法,例子中访问顺序列表为[D,B,C,A],先看B中有没有foo1(),没有则看B的上后一个类C,有的话调用。
python继承 super()的更多相关文章
- python 继承中的super
python继承中子类访问父类的方法(包括__init__)主要有两种方法,一种是调用父类的未绑定方法,另一种是使用super(仅仅对于新式类),看下面的两个例子: #coding:utf-8 cla ...
- python继承
Python继承 继承实例: 父类和子类的关系: 继承树: 没有父类就继承object类,不要忘记调用super().__init__来初始化父类 代码: class Person(object): ...
- python之super()函数
python之super()函数 python的构造器奇特, 使用魔方. 构造器内对基类对象的初始化同样也很奇特, 奇特到没有半点优雅! 在构造器中使用super(class, instance)返回 ...
- 由Python的super()函数想到的
python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...
- Python: 你不知道的 super
https://segmentfault.com/a/1190000007426467 Python: 你不知道的 super 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我 ...
- python中super的理解(转)
原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...
- python继承和多态
继承 目标 单继承 多继承 面向对象三大特性 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用,相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,产生不 ...
- day27 多继承 super 详细用法
# 没有使用super的时候的多继承,如果父类的名字变了.或者有什么更改,需要全部都一起改 class FooParent: def bar(self, message): print(message ...
- python 继承与多重继承
当然,如果不支持python继承,语言特性就不值得称为“类”.派生类定义的语法如下所示: <statement-1> . . . <statement-N> 名称 BaseCl ...
随机推荐
- UNP学习笔记1——基本TCP套接字编程
1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...
- ZBrush中SnakeHook蛇钩笔刷介绍
不同笔刷用着不同的作用,绘画出来的效果也是千姿百态,各有千秋,有些笔刷在使用的时候可以替代,但有些笔刷是无法替代,不可超越的,比如ZBrush®中给我们提供的,SnakeHook笔刷,该笔刷在模型表面 ...
- mySql 使用 SQL 文件脚本 failed to open file 注意事项
1.路径不要有中文,其实最好是全英文 2.路径可以有空格 3.路径两头不要加引号 4.作为一个MySQL命令,source C:/lib/a.sql; 后边的分号是要的. 5.使用 unix 路径风格 ...
- JS对以对象组成的数组去重
这是从https://segmentfault.com/q/1010000006954351参考的,达到了我要去重的功能!!! var hash = {};//arr是要去重的对象数组 arr = a ...
- CentOS 7 安装配置MySQL
环境 CentOS Linux release 7.5.1804 (Core) MySQL:mysql80-community-release-el7-1 检查: 在centos7中默认的是maria ...
- Tensorflow学习笔记——Summary用法
tensorboard 作为一款可视化神器,可以说是学习tensorflow时模型训练以及参数可视化的法宝. 而在训练过程中,主要用到了tf.summary()的各类方法,能够保存训练过程以及参数分布 ...
- vue svg的使用
项目要求: 需要把websocket推送的数据进行展示.不停地刷掉旧的数据.但是需要根据数据坐标圈出来对应的车辆. 开始使用的是canvas进行画图,思路是使用absolute定位,for循环,在图片 ...
- crm 系统项目(三) 业务
1. 项目背景 crm系统是某教育平台正在使用的项目,系统主要为 销售部.运营部.教质部门提供管理平台,随着公司规模的扩展,对公司员工的业务信息量化以及信息化建设越来越重要. crm系统为不同角色的用 ...
- Collection 和 Collections 的差别?
Collection 是 java.util 下的接口,它是各种集合的父接口,继承于它的 接口主要有 Set 和 List:Collections 是个 java.util 下的类.是针对集合的 帮助 ...
- perl getopt 用法
我们在linux经常常使用到一个程序须要增加參数,如今了解一下perl中的有关控制參数的函数.getopt.在linux有的參数有二种形式.一种是--help,还有一种是-h.也就是-和--的分别.- ...