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
Python set 的pop()方法 返回元素并不随机
Python set 的pop()方法 返回元素并不随机
|
1天前
|
自然语言处理 Python
python技巧:数组排序sort,all方法
python技巧:数组排序sort,all方法
|
1天前
|
程序员 Python
GitHub爆赞!最适合新手入门的教程——笨方法学Python 3
“Python 是一门既容易上手又强大的编程语言。”这句话本身并无大碍,但需要注意的是,正因为它既好学又好用,所以很多 Python 程序员只用到了其强大功能的一小部分。 今天给小伙伴们分享的这份手册以习题的方式引导读者一步一步学习编程,从简单的打印一直讲到完整项目的实现。
|
2天前
|
Python
Python基础教程(第3版)中文版 第9章 魔法方法、特性和迭代器(笔记)
Python基础教程(第3版)中文版 第9章 魔法方法、特性和迭代器(笔记)
|
2天前
|
Linux Python
在Linux下升级到Python3的两种方法
在Linux下升级到Python3的两种方法
16 0
|
2天前
|
机器学习/深度学习 算法 Python
介绍文本分类的基本概念、常用方法以及如何在Python中使用机器学习库进行文本分类
【6月更文挑战第13天】文本分类是机器学习在数字化时代的关键应用,涉及文本预处理、特征提取和模型训练等步骤。常见方法包括基于规则、关键词和机器学习,其中机器学习(如朴素贝叶斯、SVM、深度学习)是主流。在Python中,可使用scikit-learn进行文本分类,例如通过TF-IDF和朴素贝叶斯对新闻数据集进行处理和预测。随着技术发展,未来将深入探索深度学习和多模态数据在文本分类中的应用。
8 2
|
3天前
|
数据可视化 算法 JavaScript
使用Python进行网络数据可视化的多种方法与技巧
在当今信息爆炸的时代,网络数据量呈指数级增长,了解和分析这些数据对于许多领域的决策制定至关重要。可视化是理解和解释大量数据的强大工具之一,而Python作为一种流行的编程语言,提供了丰富的库和工具来进行网络数据可视化。本文将介绍一些使用Python进行网络数据可视化的方法与技巧,并提供相应的代码实例。
|
3天前
|
Python
别再被Python的面向对象编程绕晕了!一篇文章带你理清思路!
【6月更文挑战第12天】了解Python的面向对象编程(OOP)至关重要,它基于“万物皆对象”的理念,通过类(Class)定义对象的属性和方法。对象是类的实例,具有状态和行为。OOP包括类、对象、属性和方法四个基本概念。例如,`class Dog`定义了一个有`name`和`age`属性及`bark`方法的类。OOP提供代码重用、模块化、可扩展性和易理解性等优势,是现代编程的常用范式。通过学习,你可以更好地掌握这一核心技能。
|
4天前
|
Python
python的文件对象的方法
【6月更文挑战第6天】
11 3
|
5天前
|
SQL 算法 数据可视化
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】