多继承、经典类与新式类、新式类的C3算法详解

多继承
Python2版本
class A(object) ---> 新式类
class B ---> 经典类

Python3版本
默认都是新式类

经典类和新式类的不同在于多继承时继承的顺序不同
经典类 ---> 深度优先
新式类 ---> c3算法


class Immortal:

    def __init__(self):
        self.age = "长生不老"

    def fly(self):
        print("神仙都会飞")

    def eat(self):
        print("神仙也要吃东西")

class Monkey:

    def climb(self):
        print("猴子都会爬树")

    def eat(self):
        print("猴子也要进食")

class Monkey_sun(Immortal, Monkey):
    pass

sun = Monkey_sun()
sun.fly()  # 神仙都会飞
sun.climb()  # 猴子都会爬树
sun.eat()  # 神仙也要吃东西
# --->先计算Monkey_sun(Immortal, Monkey)中最左边的父类

 

# 前提是Python2版本
# 经典类的多继承
class A: pass class B(A): pass class C(A): pass class D(B, C): pass class E: pass class F(D, E): pass class G(F, D): pass class H: pass class Foo(H, G): pass # 深度优先,从左到右,一条路走到底

 

 

 继承关系图已经有了. 那如何进⾏查找呢? 记住⼀个原则. 在经典类中采⽤的是深度优先,遍历⽅案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个.

 

如图看出是按照123456这样的顺序来送. 这就叫深度优先遍历.

所以上面Python2版本中的多继承的顺序为:

类的MRO:  Foo-> H -> G -> F -> E -> D -> B -> A -> C.

 

# 新式类的多继承
# mro序列
# MRO是一个有序列表L,在类被创建时就计算出来。

# 通用计算公式为:
# mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1),
# mro(Base2), [ Base1, Base2] )(其中Child继承自Base1, Base2)

class A:
    pass

class B:
    pass

class C(B, A):
    pass

# mro(C) = mro(C(B, A)) = [C] + merge(mro(A), mro(B), [B, A])

 

如果继承至一个基类:class B(A)
这时B的mro序列为:

mro( B ) = mro( B(A) )
= [B] + merge( mro(A) + [A] )
= [B] + merge( [A] + [A] )
= [B,A]

如果继承至多个基类:class B(A1, A2, A3 …)
这时B的mro序列

mro(B) = mro( B(A1, A2, A3 …) )
= [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
= ...

计算结果为列表,列表中至少有一个元素即类自己
如上述示例[A1,A2,A3]。merge操作是C3算法的核心。

表头和表尾
表头:
  列表的第一个元素

表尾:
  列表中表头以外的元素集合(可以为空)

示例
  列表:[A, B, C]
  表头是A,表尾是B和C

如计算merge( [E,O], [C,E,F,O], [C] )
有三个列表 : ① ② ③

1 merge不为空,取出第一个列表列表①的表头E,进行判断
各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表
2 取出列表②的表头C,进行判断
C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除
merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
3 进行下一次新的merge操作 ......

 

举例:

 

 

# 上面的字母代表类
# 那么,整个多继承关系相当于下面

class O:
    pass

class D(O):
    pass

class E(O):
    pass

class F(O):
    pass

class B(D,E):
    pass

class C(E,F):
    pass

class A(B, C):
    pass

obj = A()
# 这里假设每个类中都有一个 func(),现在要算继承顺序

 

 下面是详细推导过程

1. 首先列出 mor 等式 (最开始,类A继承了类B和类C)
mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C])

2. 写出 mor(B)的结果(看上面的图)
mro(B) = mro(B(D,E))---> 类B继承了类D和类E

3. 写出 mor(B(D,E)) 的等式
mro(B(D,E)) = [B] + merge(mro(D),mro(E),[D,E])

4. 把 mor(D) 和 mor(E) 的结果写出来
因为类D和类E都只继承一个类O,所以一下就能得出结果
mro(B(D,E)) = [B] + merge([D,O],[E,O],[D,E])

5. 现在开始推导 merge([D,O],[E,O],[D,E]) 的结果
首先,有三个列表,把第一个列表的表头即D取出来,
把剩下的两个列表的表头也去掉,还剩 [O] [O] [E],
这三个列表中都没有D,因此把D加到[B],然后把列表中有D的去掉D
mro(B(D,E)) = [B,D] + merge([O],[E,O],[E])

6. 推导merge([O],[E,O],[E]),把第一个列表的表头O提出来,
三个列表还剩下 [] [O] [], 第二个列表中有O,跳过,
([O],[E,O],[E]) 第二个列表开始推导,把E提出来,
三个列表还剩下 [] [O] [],都没有E,因此把E加到[B,D]
另外,把所有列表中有E的去掉
mro(B(D,E)) = [B,D,E] + merge([O],[O])

7. 剩下[O],[O]结果就直接是O了,因此最后结果如下所示
mro(B(D,E)) = [B,D,E,O]

8. 按照上面方法推导mro(C) = mro(C(E,F)),过程如下:
mro(C) = mro(C(E,F))
mro(C(E,F)) = [C] + merge([E,O],[F,O],[E,F])
mro(C(E,F)) = [C,E] + merge([O],[F,O],[F])
mro(C(E,F)) = [C,E,F] + merge([O],[O])
mro(C(E,F)) = [C,E,F,O]

9. 接下来算总的,方法一样
mro(A(B,C)) = [A] + merge([B,D,E,O],[C,E,F,O],[B,C])
mro(A(B,C)) = [A,B] + merge([D,E,O],[C,E,F,O],[C])
mro(A(B,C)) = [A,B,D] + merge([E,O],[C,E,F,O],[C])
mro(A(B,C)) = [A,B,D,C] + merge([E,O],[E,F,O])
mro(A(B,C)) = [A,B,D,C,E] + merge([O],[F,O])
mro(A(B,C)) = [A,B,D,C,E,F] + merge([O],[O])
mro(A(B,C)) = [A,B,D,C,E,F,O] ---> 最终结果

上面结果即是新式类的继承顺序

 

# 对于继承中的 super来说,也一样
# super 也遵循 mro 顺序

class O:
    def func(self):
        print('in O')

class D(O):
    # pass
    def func(self):
        print('in D')

class E(O):
    # pass
    def func(self):
        print('in E')

class F(O):
    def func(self):
        print('in F')

class B(D,E):
    # pass
    def func(self):
        print('in B')

class C(E,F):
    # pass
    def func(self):
        print('in C')

class A(B,C):
    def func(self):
        super().func()
        print('in A')

obj = A()
obj.func()
# in B
# in A

# mro(A(B,C)) = [A,B,D,C,E,F,O]
# 根据上面的结果,如果把类B的 func() 注释掉,即结果变为
# in D
# in A

# 同理,再把类D的 func() 注释,则结果变为
# in C
# in A

# 依次类推,因此,super 也遵循 mro 顺序


# 当然,真正做项目时可以直接调用 __mro__ 方法来查询顺序
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>,
# <class '__main__.D'>, <class '__main__.C'>,
# <class '__main__.E'>, <class '__main__.F'>,
# <class '__main__.O'>, <class 'object'>)

 

posted @ 2019-01-16 19:14  星满夜空  阅读(321)  评论(0编辑  收藏  举报