zoukankan      html  css  js  c++  java
  • C3算法

    一、基本概念

    1. mro序列

    MRO是一个有序列表L,在类被创建时就计算出来。

    通用计算公式为:

    1
    2
    mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2),  [ Base1, Base2] )
    (其中Child继承自Base1, Base2)

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

    1
    2
    3
    4
    mro( B ) = mro( B(A) )
    = [B] + merge( mro(A) + [A] )
    = [B] + merge( [A] + [A] )
    = [B,A]

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

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

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

    2. 表头和表尾:

    表头: 列表的第一个元素

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

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

    3. 列表之间的+操作

    +操作:

    1
    2
    [A] + [B] = [A, B]
    (以下的计算中默认省略)

    3. merge操作:

    merge操作流程图: 

    merge操作示例:

    如计算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 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的mro
    拆
    L(H) = H + L(G) + L(F)
    
    L(G) = G + L(E)
    L(F) = F + L(D) + L(E)
    
    L(E) = E + L(C) + L(A)
    L(D) = D + L(B) + L(C) 
    
    L(C) = C + A
    L(B) = B + A
    
    L(A) = A
    
    
    
    合  +就是merge(元组,元组,元组,...)
    
    L(H) = H + L(G) + L(F) => H+(GECA)+(FDBECA)  =>HGFDBECA
    
    L(G) = G + L(E)        => G+(ECA)           =>GECA
    L(F) = F + L(D) + L(E) => F+(DBCA)+(ECA)    =>FDBECA
    
    L(E) = E + L(C) + L(A) => E+(CA)+A          =>ECA
    L(D) = D + L(B) + L(C) => D+(BA)+(CA)       =>DBCA
    
    L(C) = C + A =>CA
    L(B) = B + A =>BA
    
    L(A) = A 
    
    """
    
    """
    经典类的MRO使用的是深度优先遍历
    新式类:C3算法
    如果继承关系中没有菱形继承(深度优先就够了)
    如果有菱形:使用C3算法来计算MRO
    假设C3算法是L(x)=x的继承关系
    
    先拆分,拆到你能看到结果为止,反着进行merge()运算
    merge(元组,元组,元组,...)
    
    摘头
    头和尾在比对,如果下一个尾中没有这个头,头就出现,
    如果头在后面的尾出现,跳过该元组,继续下一个头。
    直到最后一个元组,跟自己去匹配
    """
    
    #################################################
    
    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 N:
        pass
    
    class O:
        pass
    
    class M(N,O):
        pass
    
    class G(E,M):
        pass
    
    class H(G,F):
        pass
    
    """
    H的mro
    拆
    L(H) = H + L(G) + L(F)
    
    L(G) = G + L(E) + L(M)
    L(F) = F + L(D) + L(E)
    
    L(M) = M + L(N) + L(O)
    L(E) = E + L(C) + L(A)
    L(D) = D + L(B) + L(C) 
    
    L(N) = N
    L(C) = C + A
    L(B) = B + A
    
    L(A) = A
    
    
    
    合  +就是merge(元组,元组,元组,...)
    
    L(H) = H + L(G) + L(F) => H+(GECAMNO)+(FDBECA)  =>HGFDBECAMNO
    
    L(G) = G + L(E) + L(M) => G+(ECA)+(MNO)     =>GECAMNO 
    L(F) = F + L(D) + L(E) => F+(DBCA)+(ECA)    =>FDBECA
    
    L(E) = E + L(C) + L(A) => E+(CA)+A          =>ECA
    L(M) = M + L(N) + L(O) => M+N+O             =>MNO
    L(D) = D + L(B) + L(C) => D+(BA)+(CA)       =>DBCA
    
    L(N) = N
    L(O) = O
    L(C) = C + A =>CA
    L(B) = B + A =>BA
    
    L(A) = A 
    
    """
    View Code

    二、实例

    1. 计算实例1

    示例:(多继承UML图,引用见参考) 
    多继承UML图: 
    备注:O==object

    如何计算mro(A) ?

    mro(A) = mro( A(B,C) )
    
    原式= [A] + merge( mro(B),mro(C),[B,C] )
    
      mro(B) = mro( B(D,E) )
             = [B] + merge( mro(D), mro(E), [D,E] )  # 多继承
             = [B] + merge( [D,O] , [E,O] , [D,E] )  # 单继承mro(D(O))=[D,O]
             = [B,D] + merge( [O] , [E,O]  ,  [E] )  # 拿出并删除D
             = [B,D,E] + merge([O] ,  [O])
             = [B,D,E,O]
    
      mro(C) = mro( C(E,F) )
             = [C] + merge( mro(E), mro(F), [E,F] )
             = [C] + merge( [E,O] , [F,O] , [E,F] )
             = [C,E] + merge( [O] , [F,O]  ,  [F] )  # 跳过O,拿出并删除
             = [C,E,F] + merge([O] ,  [O])
             = [C,E,F,O]
    
    原式= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C])
        = [A,B] + merge( [D,E,O], [C,E,F,O],   [C])
        = [A,B,D] + merge( [E,O], [C,E,F,O],   [C])  # 跳过E
        = [A,B,D,C] + merge([E,O],  [E,F,O])
        = [A,B,D,C,E] + merge([O],    [F,O])  # 跳过O
        = [A,B,D,C,E,F] + merge([O],    [O])
        = [A,B,D,C,E,F,O]
    

    2. 实例代码测试

    对于以上计算,用代码来测试。

    class D: pass
    class E: pass
    class F: pass
    class B(D,E): pass
    class C(E,F): pass
    class A(B,C): pass
    
    print("从A开始查找:")
    for s in A.__mro__:
        print(s)
    
    print("从B开始查找:")
    for s in B.__mro__:
        print(s)
    
    print("从C开始查找:")
    for s in C.__mro__:
        print(s)

    结果:

    从A开始查找:
    <class '__main__.A'>
    <class '__main__.B'>
    <class '__main__.D'>
    <class '__main__.C'>
    <class '__main__.E'>
    <class '__main__.F'>
    <class 'object'>
    从B开始查找:
    <class '__main__.B'>
    <class '__main__.D'>
    <class '__main__.E'>
    <class 'object'>
    从C开始查找:
    <class '__main__.C'>
    <class '__main__.E'>
    <class '__main__.F'>
    <class 'object'>

    三、总结
    每次判断如何读取都要这么麻烦计算吗?可有简单方法?
    我对此做了一个简单总结。

    1. 规律总结
    如何快速判断查找规律?

    从 “当前子类” 向上查找它的父类,
    若 “当前子类” 不是 “查找的父类” 的最后一个继承的子类时,则跳过该 “查找的父类” 的查找,开始查找 “当前子类” 的下一个父类
    查找规律流程图:

    2. 规律测试

    实例2:

    对于如下继承: 
    这里写图片描述

    通过如下判断模式:

    代码测试:

    class A1: pass
    class A2: pass
    class A3: pass
    class B1(A1,A2): pass
    class B2(A2): pass
    class B3(A2,A3): pass
    class C1(B1): pass
    class C2(B1,B2): pass
    class C3(B2,B3): pass
    class D(C1, C2, C3): pass
    
    print("从D开始查找:")
    for s in D.__mro__:
        print(s)
    
    print("从C3开始查找:")
    for s in C3.__mro__:
        print(s)

    结果:

    从D开始查找:
    <class '__main__.D'>
    <class '__main__.C1'>
    <class '__main__.C2'>
    <class '__main__.B1'>
    <class '__main__.A1'>
    <class '__main__.C3'>
    <class '__main__.B2'>
    <class '__main__.B3'>
    <class '__main__.A2'>
    <class '__main__.A3'>
    <class 'object'>
    从C3开始查找:
    <class '__main__.C3'>
    <class '__main__.B2'>
    <class '__main__.B3'>
    <class '__main__.A2'>
    <class '__main__.A3'>
    <class 'object'>
  • 相关阅读:
    [调参]batch_size的选择
    [调参]CV炼丹技巧/经验
    [Pytorch]Pytorch加载预训练模型(转)
    [PyTorch]论文pytorch复现中遇到的BUG
    [Opencv]图像的梯度与边缘检测(转)
    freemodbus移植、实例及其测试方法
    eclipse的C/C++开发搭建
    ROS安装
    U-boot移植
    QT开发实战一:图片显示
  • 原文地址:https://www.cnblogs.com/bubu99/p/12657564.html
Copyright © 2011-2022 走看看