python基础学习笔记—— 多继承
本节主要内容:
1.python多继承
2.python经典类的MRO
3.python新式类的MRO、C3算法
4.super是什么鬼?
一、python多继承
在前⾯的学习过程中. 我们已经知道了Python中类与类之间可以有继承关系. 当出现了x是
⼀种y的的时候. 就可以使⽤继承关系. 即"is-a" 关系. 在继承关系中. ⼦类⾃动拥有⽗类中除
了私有属性外的其他所有内容. python⽀持多继承. ⼀个类可以拥有多个⽗类.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
class ShenXian: # 神仙 def fei(self): print("神仙都会⻜")class Monkey: # 猴 def chitao(self): print("猴⼦喜欢吃桃⼦")class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是⼀只猴 passsxz = SunWukong() # 孙悟空sxz.chitao() # 会吃桃⼦sxz.fei() # 会⻜ |
此时,孙悟空是一只猴子,同时也是一个神仙。那孙悟空继承了这两个类。孙悟空自然就可以执行这两个类中的方法。
多继承中,存在这样一个问题。当两个父类中出现了重名方法的时候。这时怎么办?这就涉及到如何查找父类方法的这么一个问题即MRO(method resoluthion order)问题。在python中这是一个很复杂的问题。因为在不同的python版本中使用的是不同的算法来完成MRO的。首先,我们目前见到的有两个版本:
1.python 2
在python2中存在两种类
一个叫经典类。在python2.2之前。一直使用的是经典类。经典类在基类的根如果什么都不写。表示继承xxx
一个叫新式类。在python2.2之后出现了新式类。新式类的特点是基类的根是object
2.python 3
在python 3种使用的都是新式类。如果基类谁都不继承,那这个类会默认继承object
二、经典类的MRO
虽然在python 3中已经不存在经典类了。但是经典类MRO最好还是学一学。这是一种树形结构遍历的一个最直接的案例。在python的继承体系中。我们可以把类与继承关系化成一个树形结构的图。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class A: passclass B(A): passclass C(A): passclass D(B, C): passclass E: passclass F(D, E): passclass G(F, D): passclass H: passclass Foo(H, G): pass |
对付这样的MRO。很简单。画图即可:

继承关系图已经有了。那如何进行查找呢?记住一个原则。在经典类中采用的是深度优先遍历方案。什么是深度优先。就是一条路走到头。然后再回来。继续找下一个。比如。有一个快递员。去给每家每户送快递。

图中每个圈都是准备要送鸡蛋的住址。箭头和黑线表示线路。送快递的顺序告诉你入口再最下面R,并且必须从左往右送。那怎么送呢?

如图。肯定是按照123456这样的顺序来送。那这样的顺序就叫深度优先遍历。而如果是1423456呢?这种被成为广度优先遍历。MRO是什么呢?很简单从头开始。从左往右。一条路跑到头,然后回头。继续一条路跑到头。就是经典类的MRO算法。
类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C. 你猜对了么?
三、新式类的MRO
python中的新式类的MRO是采用的C3算法完成的。
C3算法很简单。就看你的代码就够了。不需要去画图。而且画图也看不出来什么。不过如果写得多了是可以从图上总结一些规律出来。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class A: passclass B(A): passclass C(A): passclass D(B, C): passclass E(C, A): passclass F(D, E): passclass G(E): passclass H(G, F): pass |
首先。我们要确定从H开始找。也就是说。创建的是H的对象。
如果从H找。那找到H+H的父类的C3,我们设C3算法是L(X),即给出X类,找到X的MRO
L(H) = H + L(G) + L(F)
继续从代码中找G和F的⽗类往⾥⾯带
L(G) = G + L(E)
L(F) = F + L(D)+ L(E)
继续找E 和 D
L(E) = E + L(C) + L(A)
L(D) = D + L(B) + L(C)
继续找B和C
L(B) = B + L(A)
L(C) = C + L(A)
最后就剩下⼀个A了. 也就不⽤再找了. 接下来. 把L(A) 往⾥带. 再推回去. 但要记住. 这⾥的
+ 表⽰的是merge. merge的原则是⽤每个元组的头⼀项和后⾯元组的除头⼀项外的其他元
素进⾏比较, 看是否存在. 如果存在. 就从下⼀个元组的头⼀项继续找. 如果找不到. 就拿出来.
作为merge的结果的⼀项. 以此类推. 直到元组之间的元素都相同. 也就不⽤再找了.
L(B) =(B,) + (A,) -> (B, A)
L(C) =(C,) + (A,) -> (C, A)
继续带.
L(E) = (E,) + (C, A) + (A) -> E, C, A
L(D) = (D,) + (B, A) + (C, A) -> D, B, A
继续带.
L(G) = (G,) + (E, C, A) -> G, E, C, A
L(F) = (F,) + (D, B, A) + (E, C, A) -> F, D, B, E, C, A
加油,最后了
L(H) = (H, ) + (G, E, C, A) + ( F, D, B, E, C, A) -> H, G, F, D, B, E, C, A
算完了. 最终结果 HGFDBECA. 那这个算完了. 如何验证呢? 其实python早就给你准备好
了. 我们可以使⽤类名.__mro__获取到类的MRO信息.
|
1
2
3
4
5
|
print(H.__mro__)结果:(<class '__main__.H'>, <class '__main__.G'>, <class '__main__.F'>, <class'__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class'__main__.C'>,<class '__main__.A'>, <class 'object'>) |
结果OK. 那既然python提供了. 为什么我们还要如此⿇烦的计算MRO呢? 因为笔
试.......你在笔试的时候, 是没有电脑的. 所以这个算法要知道. 并且简单的计算要会. 真是项⽬
开发的时候很少有⼈这么去写代码.
这个说完了. 那C3到底怎么看更容易呢? 其实很简单. C3是把我们多个类产⽣的共同继
承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要⼤家⾃⼰多写多画图就
能感觉到了. 但是如果没有所谓的共同继承关系. 那⼏乎就当成是深度遍历就可以了.
四、super是什么鬼?
super()可以帮我们执⾏MRO中下⼀个⽗类的⽅法. 通常super()有两个使⽤的地⽅:
1.可以访问父类的构造方法
2.当子类方法想调用父类(FRO)中的方法
第一种:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Foo: def __init__(self, a, b, c): self.a = a self.b = b self.c = cclass Bar(Foo): def __init__(self, a, b, c, d): super().__init__(a, b, c) # 访问⽗类的构造⽅法 self.d = db = Bar(1, 2, 3, 4)print(b.__dict__)结果:{'a': 1, 'b': 2, 'c': 3, 'd': 4} |
这样就方便了子类。不需要写那么多了。直接用父类的构造帮我们完成一部分代码
第二种:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Foo: def func1(self): super().func1() # 此时找的是MRO顺序中下⼀个类的func1()⽅法 print("我的⽼家. 就住在这个屯")class Bar: def func1(self): print("你的⽼家. 不在这个屯")class Ku(Foo, Bar): def func1(self): super().func1() # 此时super找的是Foo print("他的⽼家. 不知道在哪个屯")k = Ku() # 先看MRO . KU, FOO, BAR objectk.func1()k2 = Foo() # 此时的MRO. Foo objectk2.func1() # 报错 |
最后,这是一个面试题
MRO+super面试题
|
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
|
class Init(object): def __init__(self, v): print("init") self.val = vclass Add2(Init): def __init__(self, val): print("Add2") super(Add2, self).__init__(val) print(self.val) self.val += 2class Mult(Init): def __init__(self, val): print("Mult") super(Mult, self).__init__(val) self.val *= 5class HaHa(Init): def __init__(self, val): print("哈哈") super(HaHa, self).__init__(val) self.val /= 5class Pro(Add2,Mult,HaHa): # passclass Incr(Pro): def __init__(self, val): super(Incr, self).__init__(val) self.val+= 1# Incr Pro Add2 Mult HaHa Initp = Incr(5)print(p.val)c = Add2(2)print(c.val) |
提示,先算MRO。然后看清楚self是谁。
结论:不管super()写在哪。在哪儿执行。一定先找到MRO列表。根据MRO列表的顺序往下找。否则一切都是错的。
python基础学习笔记—— 多继承的更多相关文章
- python基础学习笔记——单继承
1.为什么要有类的继承性?(继承性的好处)继承性的好处:①减少了代码的冗余,提供了代码的复用性②提高了程序的扩展性 ③(类与类之间产生了联系)为多态的使用提供了前提2.类继承性的格式:单继承和多继承# ...
- 0003.5-20180422-自动化第四章-python基础学习笔记--脚本
0003.5-20180422-自动化第四章-python基础学习笔记--脚本 1-shopping """ v = [ {"name": " ...
- python 基础学习笔记(1)
声明: 本人是在校学生,自学python,也是刚刚开始学习,写博客纯属为了让自己整理知识点和关键内容,当然也希望可以通过我都博客来提醒一些零基础学习python的人们.若有什么不对,请大家及时指出, ...
- Python 基础学习笔记(超详细版)
1.变量 python中变量很简单,不需要指定数据类型,直接使用等号定义就好.python变量里面存的是内存地址,也就是这个值存在内存里面的哪个地方,如果再把这个变量赋值给另一个变量,新的变量通过之前 ...
- Python基础学习笔记(十三)异常
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-exceptions.html Python用异常对象(excep ...
- Python基础学习笔记(十二)文件I/O
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-files-io.html ▶ 键盘输入 注意raw_input函 ...
- Python基础学习笔记(十一)函数、模块与包
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-functions.html 3. http://www.liao ...
- Python基础学习笔记(十)日期Calendar和时间Timer
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-date-time.html 3. http://www.liao ...
- Python基础学习笔记(九)常用数据类型转换函数
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-variable-types.html 3. http://www ...
随机推荐
- 报错:Could not reserve enough space for object heap error
windows命令行运行某个命令时出现: 解决办法: 设置开始->控制面板->系统和安全->系统->高级系统设置->环境变量->系统变量->新建: 变量名: ...
- 多线程----Thread类,Runnable接口,线程池,Callable接口,线程安全
1概念 1.1进程 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 任务管理器中: 1.2线程 线程是进程中的一个执行单元 ...
- [20190611]记录一下github的基本用法
本文记录如何使用github创建项目并上传代码,因为有一段时间没用github了,中途又重装了系统,今天重新使用一下. 然后特地做简要记录: 1. 创建SSH Key SSH Key指一般在C:\Us ...
- escape,encodeURI,encodeURIComponent 之间的区别和使用
escape(目前已经被淘汰)是对字符串(string)进行编码(而另外两种是对URL),不会对下列字符编码 ASCII字母 数字 @*/+ 最关键的是,当你需要对URL编码时,请忘记这个方法,这 ...
- pt-table-checksum和pt-table-sync
环境:系统bsd,标准安装,ports安装的mysql. 主172.16.21.126 从172.16.21.128vi /etc/rc.conf 添加 mysql_enable="YES& ...
- 分享eclipse自动生成java注释方法
设置方法介绍: eclipse中:Windows->Preferences->Java->Code Style->Code Template->Comments,然后对应 ...
- iOS 学习随记 (一)
入行IT也已经很多年了,厌倦了Windows平台的工作, 4月初突然抽风买了台Mac就开始决定转身做iOS/OS X下的App开发了. 从适应Mac机器到开始编程没有花费太长时间,也因为有C#和Jav ...
- Python3获取大量电影信息:调用API
实验室这段时间要采集电影的信息,给出了一个很大的数据集,数据集包含了4000多个电影名,需要我写一个爬虫来爬取电影名对应的电影信息. 其实在实际运作中,根本就不需要爬虫,只需要一点简单的Python基 ...
- C基础的练习集及测试答案(1-15)
练习题:注:标有(课堂)字样的为课上练习,其他为课下练习基础题(50题)1.(课堂)编写程序,输出“XXX欢迎来到动物园!”(XXX是自己的名字). //1.(课堂)编写程序,输出“XXX欢迎来到动物 ...
- python基础教程总结9——模块,包,标准库
1. 模块 在python中一个文件可以被看成一个独立模块,而包对应着文件夹,模块把python代码分成一些有组织的代码段,通过导入的方式实现代码重用. 1.1 模块搜索路径 导入模块时,是按照sys ...