Python新式类继承的C3算法

Posted BlackMatrix

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 SubNewStyleClass(NewStyleClassB, NewStyleClassC):
    pass


if __name__ == __main__:
    print(SubNewStyleClass.mro())
    print(SubNewStyleClass.var)

从第一段代码的运行结果来看,与广度优先的算法结果恰巧相同,但也只是恰巧相同,不等于就是广度优先的算法。

[<class __main__.SubNewStyleClass>, <class __main__.NewStyleClassB>, <class __main__.NewStyleClassC>, <class __main__.NewStyleClassA>, <type object>]
New Style Class C

通过对代码进行修改可以证实:

将NewStyleClassC改为继承自object

class NewStyleClassA(object):
    var = New Style Class A


class NewStyleClassB(NewStyleClassA):
    pass


class NewStyleClassC(object):
    var = New Style Class C


class SubNewStyleClass(NewStyleClassB, NewStyleClassC):
    pass


if __name__ == __main__:
    print(SubNewStyleClass.mro())
    print(SubNewStyleClass.var)

运行代码输出结果

[<class __main__.SubNewStyleClass>, <class __main__.NewStyleClassB>, <class __main__.NewStyleClassA>, <class __main__.NewStyleClassC>, <type object>]
New Style Class A

从代码运行结果上看,并不符合广度优先的原则。

 

关于C3算法,在《Python高级编程》中是如此定义的:

取第一个列表的头,也就是L[B,object] ,如果这个头不在任何表的尾部,那么将它加到ClassD的线性化中,并且从合并中的列表里删除 ;否则查找下一个列表的头,如果是个好的表头则取出它。 需要注意的是: 表头指是第一个元素 ,尾部是指除表头之外的其它所有元素 。如[A,B,C,D,E,F],A是表头,[B,C,D,E,F]是尾部。

 

C3算法的本质就是Merge,不断地把mro()函数返回的序列进行Merge,规则如下:

1. 如果第一个序列的第一个元素,是其他序列的第一个元素,或者不再其他序列中再次出现,则将这个元素合并到最终的方法解析顺序序列中,并从当前操作的全部序列中删除。

2. 如果不符合,则跳过此元素,查找下一个列表的第一个元素,重复1的判断规则

 

使用第一段代码逐步进行方法解析:

1.先打印NewStyleClassB和NewStyleClassC的mro(),得到他们的继承顺序序列

[<class __main__.NewStyleClassB>, <class __main__.NewStyleClassA>, <class object>]
[<class __main__.NewStyleClassC>, <class object>]

2.根据C3算法逐步对继承顺序进行解析:

mro(SubNewStyleClass)
    = [SubNewStyleClass] + merge(mro(NewStyleClassB), mro(NewStyleClassC), [NewStyleClassB, NewStyleClassC])
    # 根据第一步的打印结果,可以得出
    = [SubNewStyleClass] + merge([NewStyleClassB, NewStyleClassA, object],  [NewStyleClassC, NewStyleClassA, object], [NewStyleClassB, NewStyleClassC])
    # 判断merge的当前序列第一个元素 NewStyleClassB, 在第三个序列中的第一个元素也存在,所以将其合并到最终序列并且删除:
    = [SubNewStyleClass, NewStyleClassB] + merge([NewStyleClassA, object],  [NewStyleClassC, NewStyleClassA, object], [NewStyleClassC])
    # 判断merge的当前序列第一个元素 NewStyleClassA,在第二个序列中存在,并且不为第二个序列的第一个元素,则跳过
    #  继续判断第二个序列中的第一个元素 NewStyleClassC,在第三个序列中存在,并且为第一个元素,所以将其合并到最终序列并且删除:
    = [SubNewStyleClass, NewStyleClassB, NewStyleClassC] + merge([NewStyleClassA, object], [NewStyleClassA, object])
    # 目前第一个序列的第一个元素是NewStyleClassA,所以再次对NewStyleClassA进行判断。
    # NewStyleClassA在第二个序列中存在,并且为第二个序列的第一个元素,所以将其合并到最终序列并且删除:
    = [SubNewStyleClass, NewStyleClassB, NewStyleClassC, NewStyleClassA] + merge([object], [object])
    # 最终object,在第二个序列中出现,并且为第一个元素,所以将其合并到最终的序列并且删除,得到最终的继承顺序:
    = [SubNewStyleClass, NewStyleClassB, NewStyleClassC, NewStyleClassA, object)

解析的结果和调用SubNewStyleClass.mro()方法打印出的结果是相同的:

[<class __main__.SubNewStyleClass>, <class __main__.NewStyleClassB>, <class __main__.NewStyleClassC>, <class __main__.NewStyleClassA>, <class object>]

 

使用第二段代码逐步进行方法解析:

1. 先打印NewStyleClassB和NewStyleClassC的mro(),得到他们的继承顺序序列

[<class __main__.NewStyleClassB>, <class __main__.NewStyleClassA>, <class object>]
[<class __main__.NewStyleClassC>, <class object>]

2. 根据C3算法逐步对继承顺序进行解析:

mro(SubNewStyleClass)
  = [SubNewStyleClass] + merge(mro(NewStyleClassB), mro(NewStyleClassC), [NewStyleClassB, NewStyleClassC])
  # 根据第一步的打印结果,可以得出
  = [SubNewStyleClass] + merge([NewStyleClassB, NewStyleClassA, object],  [NewStyleClassC, object], [NewStyleClassB, NewStyleClassC])
  # 判断merge的当前序列第一个元素 NewStyleClassB, 在第三个序列中的第一个元素也存在,所以将其合并到最终序列并且删除:
  = [SubNewStyleClass, NewStyleClassB] + merge([NewStyleClassA, object],  [NewStyleClassC, object], [NewStyleClassC])
  # 判断merge的当前序列第一个元素 NewStyleClassA,在后续的序列中都不存在,所以将其合并到最终的序列并且删除:
  = [SubNewStyleClass, NewStyleClassB, NewStyleClassA] + merge([object], [NewStyleClassC, object], [NewStyleClassC])
  # 判断merge的当前序列第一个元素 object,在第二个序列中出现,并且不是第一个元素,则跳过
  # 跳过object后,继续判断下个序列的第一个元素,也就是第二个序列的第一个元素NewStyleClassC,在第三个序列中出现并且为第一个元素,所以将其合并到最终的序列并且删除:
  = [SubNewStyleClass, NewStyleClassB, NewStyleClassA, NewStyleClassC] + merge([object], [object])
  # 再次判断object,在第二个序列中出现,并且为第一个元素,所以将其合并到最终的序列并且删除,得到最终的继承顺序:
  = [SubNewStyleClass, NewStyleClassB, NewStyleClassA, NewStyleClassC, object)

和调用SubNewStyleClass.mro()方法打印出的结果是相同的

[<class __main__.SubNewStyleClass>, <class __main__.NewStyleClassB>, <class __main__.NewStyleClassA>, <class __main__.NewStyleClassC>, <class object>]

 

以上是关于Python新式类继承的C3算法的主要内容,如果未能解决你的问题,请参考以下文章

多继承c3算法与网路编程

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

Python多继承解析顺序的C3线性算法流程解析

python的MRO和C3算法

python的MRO算法

继承c3,网络编程,相互通信