zoukankan      html  css  js  c++  java
  • 多继承、经典类与新式类、新式类的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'>)
  • 相关阅读:
    (一)IOC 容器:【2】@ComponentScan 自动扫描组件&指定扫描规则
    (一)IOC 容器:【11】Aware 注入Spring底层组件
    (一)IOC 容器:【10】@Autowired 自动装配
    (一)IOC 容器:【3】@Scope 设置组件作用域
    (一)IOC 容器:【8】Bean组件的生命周期
    每日日报7月13日
    每日日报7月12日
    每日日报7月11日
    Prism中的WeakEventHandlerManager
    博客开通标记
  • 原文地址:https://www.cnblogs.com/shawnhuang/p/10278457.html
Copyright © 2011-2022 走看看