iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python学习之MRO方法搜索顺序
  • 640
分享到

Python学习之MRO方法搜索顺序

2024-04-02 19:04:59 640人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录为什么会讲 MRO?什么是 MRO注意MRO 算法什么是旧式类,新式类想深入了解 C3 算法的可以看看官网旧式类 MRO 算法新式类 MRO 算法新式 MRO 算法的问题什么是单

为什么会讲 MRO?

  • 在讲多继承的时候,有讲到, 当继承的多个父类拥有同名属性、方法,子类对象调用该属性、方法时会调用哪个父类的属性、方法呢?
  • 这就取决于 python 的 MRO 了

什么是 MRO

  • MRO,method resolution order,方法搜索顺序
  • 对于单继承来说,MRO 很简单,从当前类开始,逐个搜索它的父类有没有对应的属性、方法
  • 所以 MRO 更多用在多继承时判断方法、属性的调用路径
  • Python 中针对类提供了一个内置属性__mro__可以查看方法搜索顺序

实际代码


class A:
    def test(self):
        print("AAA-test")


class B:
    def test(self):
        print("BBB-test")


# 继承了三个类,B、A、还有默认继承的 object
class C(B, A):
    ...


# 通过类对象调用,不是实例对象!
print(C.__mro__)


# 输出结果
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

  • 1.在搜索方法时,是按照__mro__的输出结果从左往右的顺序查找的
  • 2.如果在当前类(Class C)中找到方法,就直接执行,不再搜索
  • 3.如果没有找到,就查找下一个类中(Class B)是否有对应的方法,如果找到,就直接执行,不再搜素
  • 4.如果找到最后一个类(Class object)都没有找到方法,程序报错

类图

注意

其实 MRO 是涉及一个底层算法的,下面来详细讲解一下

MRO 算法

Python 发展到现在经历了三种算法

  • 旧式类 MRO 算法:从左往右,采用深度优先搜索(DFS),从左往右的算法,称为旧式类的 MRO
  • 新式类 MRO 算法:自 Python 2.2 版本开始,新式类在采用深度优先搜索算法的基础上,对其做了优化
  • C3 算法:自 Python 2.3 版本,对新式类采用了 C3 算法;由于 Python 3.x 仅支持新式类,所以该版本只使用 C3 算法

什么是旧式类,新式类

Python学习之新式类和旧式类讲解

想深入了解 C3 算法的可以看看官网

https://www.python.org/download/releases/2.3/mro/

旧式类 MRO 算法

需要在 python2 环境下运行这段代码

实际代码


# 旧式类算法
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()


# python2 下的运行结果
CommonA

类图

分析

  • 通过类图可以看到,此程序中的 4 个类是一个“菱形”继承的关系
  • 当使用 D 类实例对象访问 test() 方法时,根据深度优先算法,搜索顺序为D->B->A->C->A
  • 因此,旧式类 MRO 算法最先搜索得到 test() 方法是在 A 类里面,所以最终输出结果为 CommonA

新式类 MRO 算法

  • 为解决旧式类 MRO 算法存在的问题,Python 2.2 版本推出了新的计算新式类 MRO 的方法
  • 它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个

以上面的代码栗子来讲

  • 深度优先遍历,搜索顺序为D->B->A->C->A
  • 因为顺序中有 2 个 A,因此只保留最后一个
  • 最终搜索顺序为D->B->C->A

新式 MRO 算法的问题

虽然解决了旧式 MRO 算法的问题,但可能会违反单调性原则

什么是单调性原则?

在子类存在多继承时,子类不能改变父类的 MRO 搜索顺序,否则会导致程序发生异常

实际代码


class X(object):
    pass


class Y(object):
    pass


class A(X, Y):
    pass


class B(Y, X):
    pass


class C(A, B):
    pass

深度优先遍历后的搜索顺序为:C->A->X->object->Y->object->B->Y->object->X->object

相同取后者的搜索顺序为:C->A->B->Y->X->object

分析不同类的 MRO

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

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

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

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

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

到此这篇关于Python学习之MRO方法搜索顺序的文章就介绍到这了,更多相关Python MRO方法搜索顺序内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python学习之MRO方法搜索顺序

本文链接: https://www.lsjlt.com/news/134832.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python学习之MRO方法搜索顺序
    目录为什么会讲 MRO?什么是 MRO注意MRO 算法什么是旧式类,新式类想深入了解 C3 算法的可以看看官网旧式类 MRO 算法新式类 MRO 算法新式 MRO 算法的问题什么是单...
    99+
    2024-04-02
  • 怎么在python中实现顺序搜索
    怎么在python中实现顺序搜索?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。python主要应用领域有哪些1、云计算,典型应用OpenStack。2、WEB前...
    99+
    2023-06-14
  • Python学习之魔法方法
      Python中会看到前后都加双下划线的函数名,例如 __init__(self),这类写法在Python中具有特殊的含义。如果对象使用了这类方法中的某一个,那么这个方法将会在特殊的情况下被执行,然而几乎不会直接调用此类方法。 如果没...
    99+
    2023-01-30
    方法 魔法 Python
  • 四种Python机器学习超参数搜索方法总结
    目录原始模型GridSearchRandomizedSearchHalvingGridSearchHalvingRandomSearch总结与对比在建模时模型的超参数对精度有一定的影...
    99+
    2022-11-13
    Python超参数搜索方法 Python超参数搜索
  • 【Python排序搜索基本算法】之Dij
            Dijkstra算法和前一篇的Prim算法非常像,区别就在于Dijkstra算法向最短路径树(SPT)中添加顶点的时候,是按照ta与源点的距离顺序进行的。OSPF动态路由协议就是用的Dijkstra算法。下面还以那个图的例...
    99+
    2023-01-31
    算法 Python Dij
  • Python中导入模块的搜索顺序是什么
    Python中导入模块的搜索顺序是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象的脚本...
    99+
    2023-06-14
  • Python学习笔记3——三大结构:顺序
      自上而下,依次执行   分支的基本语法       if 条件表达式:         语句1         语句2         语句3       ......   条件表达式就是计算结果必须为布尔值的表达式   表达式后...
    99+
    2023-01-30
    三大 学习笔记 顺序
  • Python学习之循环方法详解
    目录for循环while循环拓展:列表推导式常见的推导式方法循环的继续与退出(continue与break)continue的使用break的使用循环实现九九乘法表什么是循环? &m...
    99+
    2024-04-02
  • python模块中搜索路径和顺序的示例分析
    这篇文章主要介绍python模块中搜索路径和顺序的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几乎无所不能,该语言通俗易懂...
    99+
    2023-06-14
  • PythonNumpy学习之索引及切片的使用方法
    目录1. 索引及切片2. 高级索引1. 索引及切片 数组中的元素可以通过索引以及切片的手段进行访问或者修改,和列表的切片操作一样。 下面直接使用代码进行实现,具体操作方式以及意义以代...
    99+
    2024-04-02
  • Python Pytorch学习之图像检索实践
    目录背景图像表现搜索随着电子商务和在线网站的出现,图像检索在我们的日常生活中的应用一直在增加。 亚马逊、阿里巴巴、Myntra等公司一直在大量利用图像检索技术。当然,只有当通常的信息...
    99+
    2024-04-02
  • python入门之算法学习
    前言 参考学习书籍:《算法图解》[美]Aditya Bhargava,袁国忠(译)北京人民邮电出版社,2017 二分查找 binary_search 实现二分查找的python代码...
    99+
    2024-04-02
  • Python学习之列表常用方法总结
    目录列表(元组)基本操作符回顾len()函数在列表与元组上的使用列表(元组)之间的累加与乘法in和notin在列表(元组)中的用法列表常用方法append()函数insert()函数...
    99+
    2024-04-02
  • Python学习之字典的常用方法总结
    目录字典添加与修改方法利用[]处理字典的内置函数update字典的内置函数setdefault关于字典的注意事项再强调尝试做一个小练习获取字典的key与value字典中的values...
    99+
    2024-04-02
  • Python学习之字符串常用方法总结
    目录什么是对象Python万物皆是对象字符串的索引索引[]索引[:]字符串的常用方法find()函数与index()函数startswith()函数与endswith()函数capi...
    99+
    2024-04-02
  • Python学习之集合的常用方法总结
    目录什么是集合?集合与列表的区别集合的创建方法集合的增删改add函数update函数remove函数clear函数del方法删除集合关于集合的重要说明获取两个集合交、并、差集的函数d...
    99+
    2024-04-02
  • Python学习手册之数据封装、类方法、
    在上一篇文章中,我们介绍了 Python 的内部方法、操作符重载和对象生命周期,现在我们介绍 Python 的数据封装、类方法、静态方法和属性函数。查看上一篇文章请点击:https://www.cnblogs.com/dustman/p/...
    99+
    2023-01-30
    手册 方法 数据
  • Python学习【魔术方法】
    魔术方法 Python中,所有以双下划线“__”包围的方法(即定义在类中的函数)为魔术方法Magic Method。 构造和初始化 在使用classname()创造实例化对象时,会依次执行__new__和__init__两个方法。 __...
    99+
    2023-01-31
    魔术 方法 Python
  • python搜索模块的查询方法
    小编给大家分享一下python搜索模块的查询方法,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!python的数据类型有哪些python的数据类型:1. 数字类型,包括int(整型)、long(长整型)和float(浮点型)...
    99+
    2023-06-14
  • Python学习手册之内部方法、操作符重
     在上一篇文章中,我们介绍了 Python 的类和继承,现在我们介绍 Python 的内部方法、操作符重载和对象生命周期。  查看上一篇文章请点击:https://www.cnblogs.com/dustman/p/10016359.ht...
    99+
    2023-01-30
    手册 操作 方法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作