python小兵 面向对象继承super和c3算法

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列表的顺序往下找. 否则一切都是错的

原文地址:https://www.cnblogs.com/huhuxixi/p/10322976.html

时间: 2024-11-01 17:28:38

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

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

Python垃圾回收    -- 引用计数        -- Python为每个对象维护一个引用计数        -- 当引用计数为0的 代表这个对象为垃圾    -- 标记清除        -- 解决孤立的循环引用        -- 标记根节点和可达对象        -- 不可达视为垃圾    -- 分代回收        -- 解决标记清除的效率问题        -- 0代 1代 2代        -- 阈值 (700,10,10)        -- 当调用c的接口开辟内存和

12.面向对象(继承/super/接口/抽象类)

面向对象继承与派生继承继承顺序继承原理子类调用父类的方法(super)组合接口接口的概念:接口的概念解释和使用:python中的接口:抽象类 面向对象 继承与派生 继承 什么是继承?继承是一种创建新的类的方式 class A: pass class B(A): pass 在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或者超类,新建的类称为派生类或者子类 python中类的继承分为,单继承和多继承. 查看继承的方法B.__bases__ 如果没有指定基类,python的类会默

16 继承 MRO和C3算法

一 多继承 Python中类与类之间可以有继承关系. 当出现了了x是一种y的的时候. 就可以使用继承关系. 即"is-a" 关系.  在继承关系中. 子类自动拥有父类中除了了私有属性外的其他所有内容.  python支持多继承. 一个类可以拥有多个父类. class ShenXian: # 神仙 def fei(self): print("神仙都会飞") class Monkey: # 猴 def chitao(self): print("猴?喜欢吃桃子&

python D20 多继承、C3算法、super()

# 今日大纲# 1.多继承# 继承:x是一种y的时候,可以使用继承关系."is a"# 一个类同时继承多个类(python, c++)# eg:孙悟空是猴子,还是神仙,还是妖怪 # 2.经典类的MRO# 通过树形结构的深度优先遍历# 一条道走到黑(从左往右) # 3.新式类的MRO(重点.面试题)c3算法# 先拆分# 在合并,第一项的头和后面所有项的身子(除了头以外的部分)进行比较,如果都没有就拿出来,如果出现了,就跳过到后一项,后一项查一个完在跳会原来的位置继续上述动作 # 4.su

python的继承,多继承,经典类的MRO,新式类的MRO,C3算法,super

#继承 class JiaoFu: def qd(self): print("教父带你祈祷") class Fu: def msj(self): print("alex喜欢msj") class Zi(Fu, JiaoFu): def dbj(self): print("刘伟喜欢大宝剑") z = Zi() z.msj() z.dbj() z.qd() class Base1: # Base1 object def func(self): pri

面向对象--多继承的C3算法

在python3的面向对象中,最底层的父类都默认继承Object类,所有的类都是新式类,多继承的继承顺序遵循广度优先规则,也就是C3算法. C3算法就是 计算继承顺序时,先把要计算的子类名拿出来,在加上继承的父类的继承顺序 从左往右找第一个继承的父类继承顺序,这样会出现3中情况: 1.第一位出现的父类名,并且在后面没有出现的提出来 2.第一位出现的父类在后面出现并且也都是在第一位,那也可以提出来 3.第一位出现的父类在后面出现但是不在第一位,那么我们就要找第二个继承的父类继承顺序,看他的第一位的

Python基础13_类与类型, MRO, C3算法, super()

一. python多继承 类与类型:http://www.cnblogs.com/blackmatrix/p/5594109.html 子类继承了多个父类, 当父类出现了重名方法时, 这时就涉及到查找父类方法的问题, 即MRO(method resolution order)问题 python中有两种类, 经典类和新式类 在Python2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于"新式类",都会获得所有"新式类"的特性:反

Python新式类继承的C3算法

在Python的新式类中,方法解析顺序并非是广度优先的算法,而是采用C3算法,只是在某些情况下,C3算法的结果恰巧符合广度优先算法的结果. 可以通过代码来验证下: class NewStyleClassA(object): var = 'New Style Class A' class NewStyleClassB(NewStyleClassA): pass class NewStyleClassC(NewStyleClassA): var = 'New Style Class C' class

Python 面向对象继承

一 什么是面向对象的继承 比较官方的说法就是: 继承(英语:inheritance)是面向对象软件技术当中的一个概念.如果一个类别A"继承自"另一个类别B,就把这个A称为"B的子类别",而把B称为"A的父类别"也可以称"B是A的超类".继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码.在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功