博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python学习笔记:第20天 多继承、MRO C3算法
阅读量:5348 次
发布时间:2019-06-15

本文共 5110 字,大约阅读时间需要 17 分钟。

目录

一、多继承

之前已经学习过了继承,当出现了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是通过数型结构的深度优先遍历的顺序查找,深度优先遍历的查找顺序如下:

1513667-20181114184649794-1978141944.jpg

两种继承模式在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算法感兴趣的朋友可以看先这里:

先看个例子:

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

1513667-20181114185551707-1304004533.png

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

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

class A:passclass B(A):passclass C(A):passclass D(B, C):passclass E(C, A):passclass F(D, E):passclass M(F, E):passclass N:passclass P(M,N):passclass G(P):passclass O:passclass X(O):passclass 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 = GPMFDBECANL(X) = X + L(O) + O                     # ==> X + (O) + O = XOL(P) = P + L(M) + L(N) + (MN)           # ==> P + (MFDBECA) + (N) + (MN) = PMFDBECANL(M) = M + L(F) + L(E) + (FE)           # ==> M + (FDBECA) + (ECA) + (FE) = MFDBECAL(F) = F + L(D) + L(E) + (DE)           # ==> F + (DBCA) + (ECA) + (DE) = FDBECAL(E) = E + L(C) + L(A) + (CA)           # ==> E + (CA) + A + (CA) = ECAL(D) = D + L(B) + L(C) + (BC)           # ==> D + (BA) + (CA) + (BC) = DBCAL(B) = B + L(A) + A                     # BAL(C) = C + L(A) + A                     # CAL(A) = A

结果如下:

1513667-20181114190243273-799182448.png

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

四、super

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

  1. 可以访问⽗类的构造⽅法
  2. 当⼦类⽅法想调⽤⽗类(MRO)中的⽅法
  • super的使用方法:
class super(object) |  super() -> same as super(__class__, 
) | 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 = db = 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')        superclass 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 -> Ea = A()a.func()# 我是B类的func1a.func2()# 我是C类的func

super的使用:

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

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

转载于:https://www.cnblogs.com/zpzhue1/p/9959668.html

你可能感兴趣的文章
线程安全问题
查看>>
linux的子进程调用exec( )系列函数
查看>>
MySQLdb & pymsql
查看>>
zju 2744 回文字符 hdu 1544
查看>>
【luogu P2298 Mzc和男家丁的游戏】 题解
查看>>
前端笔记-bom
查看>>
上海淮海中路上苹果旗舰店门口欲砸一台IMAC电脑维权
查看>>
Google透露Android Market恶意程序扫描服务
查看>>
给mysql数据库字段值拼接前缀或后缀。 concat()函数
查看>>
迷宫问题
查看>>
【FZSZ2017暑假提高组Day9】猜数游戏(number)
查看>>
泛型子类_属性类型_重写方法类型
查看>>
对闭包的理解
查看>>
练习10-1 使用递归函数计算1到n之和(10 分
查看>>
Oracle MySQL yaSSL 不明细节缓冲区溢出漏洞2
查看>>
Code Snippet
查看>>
zoj 1232 Adventure of Super Mario
查看>>
组合数学 UVa 11538 Chess Queen
查看>>
oracle job
查看>>
Redis常用命令
查看>>