Python - 面向对象编程 - MRO 方法搜索顺序(下)

简介: Python - 面向对象编程 - MRO 方法搜索顺序(下)

分析不同类的 MRO

  • A: A->X->Y->object
  • B: B->Y->X->object
  • C: C->A->B->X->Y->object

很明显,B、C 中间的 X、Y 顺序是相反的,就是说 B 被继承时,它的搜索顺序会被改变

在 python2 中运行这段代码的报错

image.png

在 python3 中运行这段代码的报错

image.png


C3 MRO 算法


  • 为解决前面两个算法的问题,Python 2.3 采用了 C3 方法来确定方法搜索顺序
  • 多数情况下,如果别人提到 Python 中的 MRO,指的都是 C3 算法

 

将上面第一个栗子的代码放到 python3 中运行

class A:
    def test(self):
        print("CommonA")
class B(A):
    pass
class C(A):
    def test(self):
        print("CommonC")
class D(B, C):
    pass
D().test()
# 输出结果
CommonC


简单了解下 C3 算法

以上面代码为栗子,C3 会把各个类的 MRO 等价为以下等式

  • A:L[A] = merge(A , object)
  • B:L[B] = B + merge(L[A] , A)
  • C:L[C] = C + merge(L[A] , A)
  • D:L[D] = D + merge(L[B] , L[C] , B , C)

 

了解一下:头、尾

以 A 类为栗,merge() 包含的 A 成为 L[A] 的头,剩余元素(这里只有 object)称为尾

 

merge 的运算方式

  1. 将 merge 第一个列表的头元素(如 L[A] 的头),记作 H
  2. 如果 H 出现在 merge 其他列表的头部,则将其输出,并将其从所有列表中删除
  3. 如果 H 只出现一次,那么也将其输出,并将其从所有列表中删除
  4. 如果 H 出现在 merge 其他列表的非头部,则取下一个列表的头元素记作 H,然后回到步骤二
  5. 最后回到步骤一,重复以上步骤

重复以上步骤直到列表为空,则算法结束;如果不能再找出可以输出的元素,则抛出异常

 

简单类 MRO 的计算栗子


class B(object): pass
print(B.__mro__)
(<class '__main__.B'>, <class 'object'>)


MRO 计算方式

L[B] = L[B(object)]
     = B + merge(L[object])
     = B + L[object]
     = B object


单继承 MRO 的计算栗子


# 计算 MRO
class B(object): pass
class C(B): pass
print(C.__mro__)
(<class '__main__.C'>, <class '__main__.B'>, <class 'object'>)


MRO 计算方式

L[C] = C + merge(L[B])

    = C + L[B]

    = C B object

 

多继承 MRO 的计算栗子


O = object
class F(O): pass
class E(O): pass
class D(O): pass
class C(D, F): pass
class B(D, E): pass
class A(B, C): pass
print(C.__mro__)
print(B.__mro__)
print(A.__mro__)
# 输出结果
(<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>)
(<class '__main__.B'>, <class '__main__.D'>, <class '__main__.E'>, <class 'object'>)
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)


O 类、object 类 MRO 计算

L[O] = O = object

 

D、E、F 类 MRO 计算

L[D] = D + merge(L[O])

       = D O

 

C 类 MRO 计算

L[C] = L[C(D, F)]
     = C + merge(L[D], L[F], DF)
     # 从前面可知 L[D] 和 L[F] 的结果
     = C +  merge(DO, FO, DF)
     # 因为 D 是顺序第一个并且在几个包含 D 的 list 中是 head,
     # 所以这一次取 D 同时从列表中删除 D
     = C + D + merge(O, FO, F)
     # 因为 O 虽然是顺序第一个但在其他 list (FO)中是在尾部, 跳过
     # 改为检查第二个list FO
     # F 是第二个 list 和其他 list 的 head
     # 取 F 同时从列表中删除 F
     = C + D + F + merge(O)
     = C D F O


B 类 MRO 计算

L[B] = L[B(D, E)]
     = B + merge(L[D], L[E], DE)
     = B + merge(DO, EO, DE)
     = B + D + merge(O, EO, E)
     = B + D + E + merge(O)
     = B D E O


A 类 MRO 计算

L[A] = L[A(B,C)]
        = A + merge(L[B], L[C], BC)
        = A + merge( BDEO, CDFO, BC )
        = A + B + merge( DEO, CDFO, C )
        # D 在其他列表 CDFO 不是 head,所以跳过到下一个列表的 头元素 C
        = A + B + C + merge( DEO, DFO )
        = A + B + C + D + merge( EO, FO )
        = A + B + C + D + E + merge( O, FO )
        = A + B + C + D + E + F + merge( O )
        = A B C D E F O


多继承 MRO 的计算栗子二


O = object
class F(O): pass
class E(O): pass
class D(O): pass
class C(D, F): pass
class B(E, D): pass
class A(B, C): pass
print(C.__mro__)
print(B.__mro__)
print(A.__mro__)
# 输出结果
(<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>)
(<class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class 'object'>)
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>)


O 类、object 类 MRO 计算

L[O] = O = object

 

D、E、F 类 MRO 计算

L[D] = D + merge(L[O])

       = D O

 

C 类 MRO 计算

L[C] = L[C(D, F)]

       = C + merge(L[D], L[F], DF)

       = C + merge(DO, FO, DF)

       = C + D + merge(O, FO, F)

       = C + D + F + merge(O)

       = C D F O

 

B 类 MRO 计算

L[B] = L[B(E, D)]

      = B + merge(L[E], L[D], ED)

      = B + merge(EO, DO, ED)

      = B + E + merge(O, DO, D)

      = B + E + D + merge(O)

      = B E D O

 

A 类 MRO 计算

L[A]  = L[A(B, C)]
        = A + merge(L[B], L[C], BC)
        = A + merge(BEDO, CDFO, BC)
        = A + B + merge(EDO, CDFO, C)
        = A + B + E + merge(DO,CDFO, C)
        = A + B + E + C + merge(O,DFO)
        = A + B + E + C + D + merge(O, FO)
        = A + B + E + C + D + F + merge(O)
        = A B E C D F O
相关文章
|
1天前
|
设计模式 算法 关系型数据库
Python面向对象编程基础解析
【7月更文挑战第21天】在Python中,面向对象编程(OOP)是一种强大的编程范式,它允许开发者通过定义类和对象来组织和管理代码。本文将介绍Python中面向对象编程的基础概念,并通过代码实例进行解析。
21 10
|
6天前
|
算法 数据处理 索引
告别低效搜索!Python中Trie树与Suffix Tree的实战应用秘籍!
【7月更文挑战第21天】探索Python中的字符串搜索效率提升:使用Trie树与Suffix Tree。Trie树优化单词查询,插入和删除,示例展示其插入与搜索功能。Suffix Tree,复杂但强大,适用于快速查找、LCP查询。安装[pysuffixtree](https://pypi.org/project/pysuffixtree/)库后,演示查找子串及最长公共后缀。两者在字符串处理中发挥关键作用,提升数据处理效率。**
|
8天前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
【7月更文挑战第19天】Trie树,又称前缀树,是优化字符串搜索的高效数据结构。通过利用公共前缀,Trie树能快速插入、删除和查找字符串。
27 2
|
14天前
|
算法 数据挖掘 数据处理
搜索新境界:Python二分查找变种实战,精准定位数据不是梦!
【7月更文挑战第13天】二分查找算法以O(log n)效率在有序数组中查找数据。基础算法通过不断分割数组对比中间元素。Python实现变种包括:1) 查找目标值的第一个出现位置,找到后向左搜索;2) 查找目标值的最后一个出现位置,找到后向右搜索。这些变种在数据分析和索引构建等场景中极具价值,提升处理效率。
|
13天前
|
Python
在Python中实现面向对象编程
【7月更文挑战第14天】在Python中实现面向对象编程
14 2
|
15天前
|
Python
二分查找变种大赏!Python 中那些让你效率翻倍的搜索绝技!
【7月更文挑战第12天】二分查找是高效搜索算法,适用于有序数组。基础原理是对比中间元素,按目标值大小在左右两侧递归查找。
20 4
|
14天前
|
数据可视化 PyTorch Serverless
Python 性能分析的几个方法,找到你代码中的那个她
我们在编写了一个脚本在笔记本上处理一些数据,然后去喝杯咖啡或者上了个厕所,15分钟后回来时发现进度才完成不到10%。 我们的脑袋里面就会发问:为什么这么慢?究竟是在哪个部分是慢的?是读取数据、处理数据还是保存数据?如何让它变快?它真的很慢吗? 有了这个疑问我们尝试去解决这个问题,下面我们介绍几个 python 性能分析的工具。
|
16天前
|
机器学习/深度学习 数据采集 算法
Python实现SSA智能麻雀搜索算法优化支持向量机回归模型(SVR算法)项目实战
Python实现SSA智能麻雀搜索算法优化支持向量机回归模型(SVR算法)项目实战
|
16天前
|
机器学习/深度学习 数据采集 算法
Python实现SSA智能麻雀搜索算法优化支持向量机分类模型(SVC算法)项目实战
Python实现SSA智能麻雀搜索算法优化支持向量机分类模型(SVC算法)项目实战
|
13天前
|
数据可视化 Python
时间序列分析是一种统计方法,用于分析随时间变化的数据序列。在金融、经济学、气象学等领域,时间序列分析被广泛用于预测未来趋势、检测异常值、理解周期性模式等。在Python中,`statsmodels`模块是一个强大的工具,用于执行各种时间序列分析任务。
时间序列分析是一种统计方法,用于分析随时间变化的数据序列。在金融、经济学、气象学等领域,时间序列分析被广泛用于预测未来趋势、检测异常值、理解周期性模式等。在Python中,`statsmodels`模块是一个强大的工具,用于执行各种时间序列分析任务。