python多继承

  在前面的学习过程中. 我们已经知道了Python中类与类之间可以有继承关系. 当出现了x是一种y的的时候. 就可以使⽤继承关系. 即"is-a" 关系. 在继承关系中. 子类⾃自动拥有⽗类中除了私有属性外的其他所有内容. python支持多继承. 一个类可以拥有多个父类.

  此时, 孙悟空是一只猴子, 同时也是一个神仙. 那孙悟空继承了这两个类. 孙悟空自然就可以执行这两个类中的方法.

  多继承用起来简单. 也很好理解. 但是多继承中, 存在着这样一个问题. 当两个父类中出
现了重名方法的时候. 这时该怎么办呢? 这时就涉及到如何查找父类方法的这么一个问题.
即MRO(method resolution order) 问题. 在python中这是一个很复杂的问题. 因为在不同的
python版本中使⽤的是不同的算法来完成MRO的. 首先. 我们目前能见到的有两个版本:

python2

在python2中存在两种类.

一个叫经典类. 在python2.2之前. 一直使用的是经典类. 经典类在基类的根如果什么都不写. 表示继承xxx.

一个叫新式类. 在python2.2之后出现了了新式类. 新式类的特点是基类的根是object

python3

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

经典类的MRO
  虽然在python3中已经不存在经典类了. 但是经典类的MRO最好还是学一学. 这是一种
树形结构遍历的一个最直接的案例例. 在python的继承体系中. 我们可以把类与类继承关系化
成⼀个树形结构的图. 来, 上代码:

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

图中每个圈都是准备要送鸡蛋的住址. 箭头和⿊线表⽰线路. 那送鸡蛋的顺序告诉你入
⼝在最下面R. 并且必须从左往右送. 那怎么送呢?

  如图. 肯定是按照123456这样的顺序来送. 那这样的顺序就叫深度优先遍历. 而如果是
142356呢? 这种被称为广度优先遍历. 好了. 深度优先就说这么多. 那么上面那个图怎么找的
呢? MRO是什么呢? 很简单. 记住. 从头开始. 从左往右. 一条路跑到头, 然后回头. 继续一条
路跑到头. 就是经典类的MRO算法.

  类的MRO: Foo-> H -> G -> F -> D -> B -> A -> C -> E. 你猜对了了么?

新式类的MRO   (重点加难点)

  python中的新式类的MRO是采用的C3算法来完成的.

  c3算法很简单. 就看你的代码就够了. 不需要去画图. 且画图也看不出来什么. 不过如
果写得多了是可以从图上总结出一些规律来的. 先看代码:

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 G(E):
pass
class H(G, F):
pass

首先. 我们要确定从H开始找. 也就是说. 创建的是H的对象.
如果从H找. 那找到G+F的父类的C3, 我们设C3算法是L(x) , 即给出x类. 找到x的MRO
  L(H) = H + L(G) + L(F) + GF
继续从代码中找G和F的⽗类往里面带
  L(G) = G + L(E) + E
  L(F) = F + L(D)+ L(E) + DE
继续找E 和 D
  L(E) = E + L(C) + L(A) + CA
  L(D) = D + L(B) + L(C) + BC
继续找B和C
  L(B) = B + L(A) + A
  L(C) = C + L(A) + A
  最后就剩下⼀个A了. 也就不用再找了. 接下来. 把L(A) 往里带. 再推回去. 但要记住. 这里的
+ 表示的是merge. merge的原则是用每个元组的头 ⼀项一项地和后⾯元组的除头一项外的其他元
素进行比较, 看是否存在. 如果存在. 就从下一个元组的头一项一项地继续找. 如果找不到. 就拿出来.
作为merge的结果的一项. 以此类推. 直到元组之间的元素都相同. 也就不用再找了.
  L(B) =(B,) + (A,) + (A) -> (B, A)
  L(C) =(C,) + (A,) + (A) -> (C, A)
继续带.
  L(E) = (E,) + (C, A) + (A) + (C,A) -> E, C, A
  L(D) = (D,) + (B, A) + (C, A) + (B, C) -> D, B, C, A
继续带.
  L(G) = (G,) + (E, C, A) + (E) -> G, E, C, A
  L(F) = (F,) + (D, B, C, A) + (E, C, A) + (D, E)-> F, D, B, E, C, A
加油, 最后了
  L(H) = (H, ) + (G, E, C, A) + ( F, D, B, E, C, A) + (G, F) -> H, G, F, D, B, E, C, A
算完了. 最终结果 HGFDBECA. 那这个算完了. 如何验证呢? 其实python早就给你准备好
了. 我们可以使⽤类名.__mro__获取到类的MRO信息.
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. 当子类方法想调用父类(MRO)中的方法

我们先看第一种:

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

  这样就⽅便了子类. 不需要写那么多了. 直接用⽗类的构造帮我们完成一部分代码

第二种:

class Foo:
def func1(self):
super().func1() # 此时找的是MRO顺序中下一个类的方法
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 object
k.func1()
k2 = Foo() # 此时的MRO. Foo object
k2.func1() # 报错

最后是一道面试题;


# MRO + super ⾯试题
class Init(object):
def __init__(self, v):
print("init")
self.val = v
class Add2(Init):
def __init__(self, val):
print("Add2")
super(Add2, self).__init__(val)
print(self.val)
self.val += 2
class Mult(Init):
def __init__(self, val):
print("Mult")
super(Mult, self).__init__(val)
self.val *= 5
class HaHa(Init):
def __init__(self, val):
print("哈哈")
super(HaHa, self).__init__(val)
self.val /= 5
class Pro(Add2,Mult,HaHa): #
pass
class Incr(Pro):
def __init__(self, val):
super(Incr, self).__init__(val)
self.val+= 1 # Incr Pro Add2 Mult HaHa Init p = Incr(5)
print(p.val)
c = Add2(2)
print(c.val)
提示. 先算MRO . 然后看清楚self是谁.

#Add2
#Mult
#哈哈
#init
#5.0
#8.0

#Add2
#init
#2
#4


结论: 不管super()写在哪儿. 在哪儿执行. 一定先找到MRO列表. 根据
MRO列表的顺序往下找. 否则一切都是错的

python小兵 面向对象继承super和c3算法的更多相关文章

  1. Python之面向对象继承和派生

    Python之面向对象继承和派生 什么是继承: 继承是一种创建新的类的方法.在Python中,新建的类可以继承自一个或多个父类.原始类称为基类或超类. 新建的类称为派生类或子类. Python中类的继 ...

  2. python学习笔记:第20天 多继承、MRO C3算法

    目录 一.多继承 二.旧式类的MRO 三.新式类的MRO 四.super 一.多继承 之前已经学习过了继承,当出现了x是⼀种y的的时候,就可以使⽤继承关系,即"is-a" 关系.在 ...

  3. python摸爬滚打之day20--多继承,MRO和C3算法

    1.新式类和经典类 在python2.2之前, 基类如果不写(), 则表示为经典类; 在python2.2之后, 经典类不复存在, 只存在新式类. 如果基类谁都不继承的话, 则默认继承object. ...

  4. python的垃圾回收机制 继承的顺序C3算法

    Python垃圾回收    -- 引用计数        -- Python为每个对象维护一个引用计数        -- 当引用计数为0的 代表这个对象为垃圾    -- 标记清除        - ...

  5. python 小兵面向对象

    Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. 如果你以前没有接触过 ...

  6. Python之面向对象继承复习

    总结:self是谁就从谁开始寻找

  7. python中多继承C3算法研究

    在python的面向对象继承问题中,单继承简单易懂,全部接受传承类的属性,并可添加自带属性, 但是,在多继承情况下,会遇到多个被继承者的顺序问题,以及多次继承后查找前几次继承者需求属性时,可能不易发现 ...

  8. Python的多继承问题-MRO和C3算法

    大部分内容转载自C3 线性化算法与 MRO 理解Python中的多继承 Python 中的方法解析顺序(Method Resolution Order, MRO)定义了多继承存在时 Python 解释 ...

  9. python 面向对象(六)MRO C3算法 super

    ########################总结################ 面向对象回顾 类:对某一个事物的描述,对一些属性和方法的归类 class 类名: var=123#类变量 def ...

随机推荐

  1. 【LeetCode】495. Teemo Attacking 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  2. 【LeetCode】556. Next Greater Element III 解题报告(Python)

    [LeetCode]556. Next Greater Element III 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人 ...

  3. 如何改善win10录屏时声音降噪(消除杂音)

    此文章是针对win10系统中安装Realtek声卡的麦克风出现杂音的设置办法 1. 打开win10的控制面板,找到"硬件和声音选项" 2. 进入"硬件和声音"选 ...

  4. Generative Adversarial Nets (GAN)

    目录 目标 框架 理论 数值实验 代码 Generative Adversarial Nets 这篇文章,引领了对抗学习的思想,更加可贵的是其中的理论证明,证明很少却直击要害. 目标 GAN,译名生成 ...

  5. springboot配置health接口

    springboot配置health接口 spring-boot-starter-actuator 健康监控配置及使用 这样是可以看到一些结果的 如果在配置文件中用了下面这个,也是可以生效的 # 不进 ...

  6. MD5,SHA1及SHA256等哈希加密方法实现:Java,C#,Golang,Python

    哈希算法又称散列算法,它可以从任何数据中快速的创建一个凭证,而这个凭证很难被推倒出来,因为一丁点的变化会导致凭证的差别恨到,也就是说哈希算法具有不可逆性,因此它在密码数据校验方面用的很广,比如我们常用 ...

  7. 年功序列c++游戏

    题目描述 在虚拟国度里多了很多 Virtual oier,为了树立对后辈的威信,从第 11 个 Virtual oier 开始的 oier 们搞起了年功序列的制度. 虚拟国度的创始人 oier Cht ...

  8. c# - 常量定义与赋值

    1.前言 c#与Java很相似,但是不一样,又与js(JavaScript)相似,但是也不一样,所以我认为c#是Java和 js的孩子. 2.常量定义 字符串: const string = &quo ...

  9. [未完] Linux 4.4 USB —— spiflash模拟usb大容量存储设备 调试记录 Gadget Mass Stroage

    linux 4.4 USB Gadget Mass Stroage 硬件平台: licheepi nano衍生 调试记录 驱动信息 │ This driver is a replacement for ...

  10. Texture+PBR两种工作流程

    一.导入Texture 1.Inpspector TextureSize 2的n次幂,底层图形学需要,计算更快:不使用2的倍数,系统也会添加像素补全2n: 有最大尺寸限制8k,cubemap最高4k: ...