一、多继承

之前已经学习过了继承,当出现了x是⼀种y的的时候,就可以使⽤继承关系,即"is-a" 关系。在继承关系中,⼦类⾃动拥有⽗类中除了私有属性外的其他所有内容,ython⽀持多继承,⼀个类可以拥有多个⽗类:

class A:
def func1(self):
print('我是A类的func1') class B:
def func2(self):
print('我是B类的func2') class C(A, B):
def func3(self):
print('我是C类的func3') c = C()
c.func1()
c.func2()
c.func3() # 结果:
# 我是A类的func1
# 我是B类的func2
# 我是C类的func3

多继承用起来虽然很简单,但是它也会带来一些问题,当两个⽗类中出现了重名⽅法的时候,即出现了二义性,这时该怎么办呢?这时就涉及到如何查找⽗类⽅法的这么⼀个问题,即MRO(method resolution order) 问题,在python中不同版本的MRO查找父类的顺序也不一样:

  • 在python2中存在这两种类:

    • ⼀个叫经典类. 在python2.2之前,⼀直使⽤的是经典类,经典类在基类的根如果什么都不写,表⽰继承xxx.
    • ⼀个叫新式类. 在python2.2之后出现了新式类,新式类的特点是基类的根是object
  • python3

    • python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承object

二、旧式类的MRO

旧式类的MRO是通过数型结构的深度优先遍历的顺序查找,深度优先遍历的查找顺序如下:

两种继承模式在DFS下的优缺点。

  • 第一种通常称为正常继承模式,两个互不相关的类的多继承,这种情况DFS顺序正常,不会引起任何问题;

  • 第二种,棱形继承模式,存在公共父类(D)的多继承这种情况下DFS必定经过公共父类(D),这时候想想,如果这个公共父类(D)有一些初始化属性或者方法,但是子类(C)又重写了这些属性或者方法,那么按照DFS顺序必定是会先找到D的属性或方法,那么C的属性或者方法将永远访问不到,导致C只能继承无法重写(override)。

旧式类MRO的查找只需要记住一个原则:在经典类中采⽤的是深度优先遍历⽅案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个

三、新式类的MRO

python中的新式类的MRO是采⽤的C3算法来完成的。

c3算法很简单,就看你的代码就够了,不需要去画图。计算方法和公式如下:

L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)       # 以下的merge函数由L代替(只是书写方式不一样)
L[object] = object
def merge(seqs):
print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
res = []; i=0
while 1:
nonemptyseqs=[seq for seq in seqs if seq]
if not nonemptyseqs: return res
i+=1; print '\n',i,'round: candidates...',
for seq in nonemptyseqs: # find merge candidates among seq heads
cand = seq[0]; print ' ',cand,
nothead=[s for s in nonemptyseqs if cand in s[1:]]
if nothead: cand=None #reject candidate
else: break
if not cand: raise "Inconsistent hierarchy"
res.append(cand)
for seq in nonemptyseqs: # remove cand
if seq[0] == cand: del seq[0] def mro(C):
"Compute the class precedence list (mro) according to C3"
return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])

以上代码摘自于python官方文档,对于python的MRO C3算法感兴趣的朋友可以看先这里:Python官方对MRO的说明

先看个例子:

class A:pass
class B:pass
class C:pass
class E(A,B):pass
class F(B,C):pass
class G(E,F):pass # 求G的MRO查找顺序?

最终结果为:GEAFBC,最终我们可以使⽤class_name.__mro__或者class_name.mro()获取到类的MRO信息。

我们再来看一个复杂的例子:

class A:pass
class B(A):pass
class C(A):pass
class D(B, C):pass
class E(C, A):pass
class F(D, E):pass
class M(F, E):pass
class N:pass
class P(M,N):pass
class G(P):pass
class O:pass
class X(O):pass
class H(G, X, F):pass # 求H的MRO?

照之前的方法处理,可以得到如下结果:


L(H) = H + L(G) + L(X) + L(F) + (GXF) # ==> H + (GPMFDBECAN) + (XO) + (FDBECA) + (GXF) # 得到最终的公式,
(FDBECAN) + (XO) + (FDBECA) + (XF) = HGPM # 先提取出HGPMX
(FDBECAN) + (O) + (FDBECA) + (F) = HGPMX # 再提取出X
(DBECAN) + (O) + (DBECA) = HGPMXF # 再提取F
====> HGPM XFDB ECANO # 最终得到结果 L(G) = G + L(P) + P # ==> G + (PMFDBECAN) + P = GPMFDBECAN
L(X) = X + L(O) + O # ==> X + (O) + O = XO
L(P) = P + L(M) + L(N) + (MN) # ==> P + (MFDBECA) + (N) + (MN) = PMFDBECAN
L(M) = M + L(F) + L(E) + (FE) # ==> M + (FDBECA) + (ECA) + (FE) = MFDBECA L(F) = F + L(D) + L(E) + (DE) # ==> F + (DBCA) + (ECA) + (DE) = FDBECA
L(E) = E + L(C) + L(A) + (CA) # ==> E + (CA) + A + (CA) = ECA L(D) = D + L(B) + L(C) + (BC) # ==> D + (BA) + (CA) + (BC) = DBCA
L(B) = B + L(A) + A # BA
L(C) = C + L(A) + A # CA
L(A) = A

结果如下:

总结一下:C3是把我们多个类产⽣的共同继承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要⼤家⾃⼰多写多画图就能感觉到了. 但是如果没有所谓的共同继承关系. 那⼏乎就当成是深度遍历就可以了

四、super

super()可以帮我们执⾏MRO中下⼀个⽗类的⽅法,通常super()有两个使⽤的地⽅:

  1. 可以访问⽗类的构造⽅法
  2. 当⼦类⽅法想调⽤⽗类(MRO)中的⽅法
  • super的使用方法:
class super(object)
| super() -> same as super(__class__, <first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)

我们先来看第一种情况:子类访问父类的构造方法:

class A:
def __init__(self, a, b):
self.a = a
self.b = b class B(A):
def __init__(self, a, b, c, d):
super(B, self).__init__(a, b) # 访问父类的构造方法
self.c = c
self.d = d
b = B(1, 2, 3, 4)
print(b.__dict__) # 结果:
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}

第二种情况:⼦类⽅法想调⽤⽗类(MRO)中的⽅法

class D:
def func(self):
print('我是D类的func') class E:
def func(self):
print('我是E类的func') class C(E):
def func(self):
print('我是C类的func') class B(D):
def func(self):
print('我是B类的func1')
super
class A(B, C):
def func(self):
super().func()
def func2(self):
super(D, self).func() # 这里可以直接调用MRO中super指定类的下一个类中的方法,即C中的func方法 # A的MRO顺序为: A -> B -> D -> C -> E a = A()
a.func()
# 我是B类的func1 a.func2()
# 我是C类的func

super的使用:

  • super()super(cls, object).functions的调用是一样的(cls是指调用super方法的那个类,不是指定另一个类而是类本身)
  • super(cls, object).functions可以指定cls,让super调用MRO中指定类的下一个类的方法(上面就是一个很好的例子)

注意:不管super()写在哪⼉,在哪⼉执⾏,⼀定先找到MRO列表,根据MRO列表的顺序往下找,否则⼀切都是错的

python学习笔记:第20天 多继承、MRO C3算法的更多相关文章

  1. python学习笔记(20)--生成点拨【已放弃】

    说明: 1. 本来是要写个脚本生成点拨rtf给讲师朗读的,不过实在是安装不上pywin32这个模块,虽然下载下来了whl文件,pip install 也能安装,但是导入pywin32.win32com ...

  2. Python学习笔记——基础篇【第五周】——算法(4*4的2维数组和冒泡排序)、时间复杂度

    目录 1.算法基础 2.冒泡排序 3.时间复杂度 (1)时间频度 (2)时间复杂度 4.指数时间 5.常数时间 6.对数时间 7.线性时间 1.算法基础  要求:生成一个4*4的2维数组并将其顺时针旋 ...

  3. Python学习笔记基础篇——总览

    Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...

  4. Python 学习笔记(下)

    Python 学习笔记(下) 这份笔记是我在系统地学习python时记录的,它不能算是一份完整的参考,但里面大都是我觉得比较重要的地方. 目录 Python 学习笔记(下) 函数设计与使用 形参与实参 ...

  5. python学习笔记整理——字典

    python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...

  6. OpenCV之Python学习笔记

    OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...

  7. 【Python学习笔记之二】浅谈Python的yield用法

    在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生 ...

  8. Python学习笔记(十四)

    Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...

  9. Python学习笔记(十)

    Python学习笔记(十): 装饰器的应用 列表生成式 生成器 迭代器 模块:time,random 1. 装饰器的应用-登陆练习 login_status = False # 定义登陆状态 def ...

随机推荐

  1. [问题记录]libpomelo的安装

    1. 描述: 按照github上的操作完成 Windows in your libpomelo project root directory open git bash and type in mkd ...

  2. window 服务注册、卸载

    1.以管理员身份打开 命令窗口 2.服务注册命令:sc create myServer binpath= path 3.服务卸载命令:sc delete myServer Topshelf 服务安装 ...

  3. C/C++ 合法整数与字符

    一.C语言中的合法整型 首先C语言中的整型有三种表示方式:十进制.八进制和十六进制.(C语言中没有表示二进制的整型) 十进制: 如 int a = 63; //一个正常的整型 八进制: 如果想用8进制 ...

  4. zt 设计模式六大原则(3):依赖倒置原则

    下面说法对不对? 父类将算法(逻辑)封装起来,子类实现细节:这个就叫DIP(依赖倒置:Dependency Inversion Principles),模板模式就是这个原则的实现.如果在父类中加一个t ...

  5. hive 的一个小问题

    hive查询语句中如果包含中文,如like '%奥巴马%' ,并且所查hive表中的数据是utf-8的,可能会查不出正确结果. 原因可能是系统环境的LANG 设置不是utf-8,需要把环境变量LANG ...

  6. EventBus事件总线

    EventBus事件总线的使用-自己实现事件总线   在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类.这对于桌面应用或者独立的windows服务 ...

  7. iText中输出 中文

    iText中输出中文,有三种方式: 1.使用iTextAsian.jar中的字体    BaseFont.createFont("STSong-Light", "UniG ...

  8. 理解HTML DOM

    DOM(Document Object Model)全称文档对象模型.DOM其实是JavaScript操作网页的一套API接口,定义了访问和操作HTML文档的标准.定义了所有HTML元素的对象和属性, ...

  9. MBTiles 离线地图演示 - 基于 Google Maps JavaScript API v3 + SQLite

    MBTiles 是一种地图瓦片存储的数据规范,它使用SQLite数据库,可大大提高海量地图瓦片的读取速度,比通过瓦片文件方式的读取要快很多,适用于Android.IPhone等智能手机的离线地图存储. ...

  10. javascript实现jsonp跨域问题+原理

    在工作中往往存在跨域的问题 ,跨域是什么概念就不在这里了,搜这类问题的肯定已经知道了.下面直接探讨jsonp跨域原理 jspon跨域原理: 1.动态创建一个script标签 var script = ...