Python继承的设计及演化

Python中的继承

文章目录

  • Python中的继承
    • 概念明确
      • MRO
      • 深度优先算法(Python2.2之前及Python2.2的经典类中使用)
      • 优化版的深度优先算法(只在Python2.2版本的新式类中使用)
      • 广度优先算法(Python任何版本都从未使用过)
      • C3算法(Python2.3开始的新式类、Python3.x使用)
    • Python中继承的三种形态
      • 单继承链
      • 无重叠的多继承链
      • 有重叠的多继承链
    • Python2.2之前
    • Python2.2
    • Python2.3-2.7
    • Python3.x
    • C3算法的分步推演
      • C3算法检测出有问题的继承

为什么要单独研究Python的继承呢?

因为Python中是存在多继承的。

在 概念明确 阶段,如果看不懂,可以先往后面看,有代码例子,你肯定会明白的。

概念明确

MRO

  • Method Resolution Order(方法解析顺序)
  • 其实就可以理解为当我们的代码想要访问资源的时候,这个资源的查找顺序。
  • 例如A类继承自B类,B类继承自C类。当我们访问A.name的时候如果A没有就会到B类中去找是否有名为name的类属性,如果B也没有则继续找C类的类属性name,如果都没找到则报错,如果过程中找到了就达到了我们的目的,这个过程就是MRO。

深度优先算法(Python2.2之前及Python2.2的经典类中使用)

  • 解释:沿着一个继承链 ,尽可能的往深了去找。
  • 具体算法步骤:
    1. 节点压入**栈(先进后出)**中
    2. 每次从栈中弹出一个元素,搜索所有在它下一级的元素(可以是1个,也可以是多个),并把所有这些下一级的元素全部压入栈中。
    3. 重复第2个步骤到结束
  • 问题:
    • 在"有重叠的多继承"中 ,违背 “重写可用原则”
      • 如下面图所示,即,在C类中重写了D的方法,但是MRO会优先使用D,这样C类的重写就失效了。
      • 即,使用深度优先算法算出来后是:A -> B -> D -> C
    • 所以在当年都是通过重新设计继承结构的手段,来避免这样的问题。

优化版的深度优先算法(只在Python2.2版本的新式类中使用)

  • 在Python2.2的新式类中使用了:优化版的深度优先算法

    • 如果产生重复元素,会保留最后一个。

    • 并且 , 更尊重基类出现的先后顺序

    • 注意,并不是"广度优先算法"

  • 问题:

    • 无法检测出有问题的继承
    • 有可能还会违背“局部优先”的原则

广度优先算法(Python任何版本都从未使用过)

  • 解释:沿着继承链,尽可能往宽了去找。
  • 具体算法步骤:
    • 把根节点放到队列的末尾
    • 每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。注意如果发现这个元素已经被处理过则略过。
    • 重复上面步骤

C3算法(Python2.3开始的新式类、Python3.x使用)

  • 一定要记住两个公式:

    • L(object) = [object]
    • L( 本类(父类1, 父类2) ) = [本类] + merge( L(父类1), L(父类2), [父类1,父类2] )
  • 解释:

    • L:表示一个函数,就是待计算的某个类的资源访问顺序所组成的列表,L也就是list。
    • L(object)就是要计算object类本身的访问顺序,那么最终的结果就是由object所组成的列表。
    • merge()函数里的传参就是找到本类的第一个父类并计算出第一个父类的资源访问顺序所组成的列表,第二个参数就是本类所继承的第二个父类所计算出来的资源访问顺序组成的列表,第三个参数就是把本类所继承的所有父类都组成一个列表。
    • +代表合并列表。
  • merge()算法:

    1. 如果第一个列表的第一个元素是:
      • 后续列表的第一个元素,则将这个元素合并到最终的解析列表中,并从当前操作的所有列表中删除。
      • 或者,后续列表中没有再次出现,则将这个元素合并到最终的解析列表中,并从当前操作的所有列表中删除。
    2. 如果1中的两个条件都不符合,则跳过此元素,查找下一个列表的第一个元素,重复1的判断规则。
    3. 如果最终无法把所有元素归并到解析列表,则报错。
  • 相信你看完公式后,还是无法理解,可以往后看例子,就会理解了。

Python中继承的三种形态

在这里插入图片描述

单继承链

  • 遵循"从下到上的原则"
  • 自己身上没有这个资源,就到父类里面去找,父类里面没有再往上找
# python3.x
import inspect
class C:name = "C"
class B(C):pass
class A(B):pass
print(A.name) # C
print(inspect.getmro(A)) # A -> B -> C -> object

无重叠的多继承链

  • 遵循"单调原则"
  • 按照继承的先后顺序 , 优先调用左侧继承链上的资源
# python3.x
import inspect
class D:name = "D"
class E:name = "E"
class C(E):name = "C"
class B(D):pass
class A(B,C):pass
print(A.name) # D
print(inspect.getmro(A)) # A -> B -> D -> C -> E -> object

有重叠的多继承链

  • 遵循"从下到上的原则"
  • A继承B同时继承C,A类没有就找B,B没有就找C,C没有才找D
# python3.x
import inspect
class D:pass
class C(D):pass
class B(D):pass
class A(B, C):pass
print(inspect.getmro(A)) # A -> B -> C -> D -> object

Python2.2之前

  • 仅仅存在经典类
  • MRO原则:深度优先(从左往右)
  • 在"有重叠的多继承"中 ,违背 “重写可用原则”
    • 即,在C类中重写了D的方法,但是MRO会优先使用D,这样C类的重写就失效了。
    • 即,使用深度优先算法算出来后是:A -> B -> D -> C
  • 所以在当年都是通过重新设计继承结构的手段,来避免这样的问题。

Python2.2

  • 在Python2.2中,为了方便的同一类的一些内置方法,就产生了object的类,把很多需要统一的方法都设计到了object类里面,这就是新式类的由来。

  • 只要继承object的类都是新式类,所以有了object类以后,就非常容易出现重叠继承的问题,为此在Python2.2中针对新式类做了一些优化。

  • 在Python2.2中,增加了inspect模块,可以方便我们快速计算MRO。

  • MRO原则:

    • 经典类:依旧采用之前的深度优先(从左到右)
    • 新式类:在深度优先(从左到右)的算法基础之上,进行优化
      • 如果产生重复元素,会保留最后一个。
      • 并且 , 更尊重基类出现的先后顺序
      • 注意,并不是"广度优先算法"
        • 假如!!用的是广度优先算法,在计算无重叠的多继承链时的顺序应该是:A -> B -> C -> D -> E -> object
        • 而真实的在Python2.2中计算无重叠的多继承链时的顺序是:A -> B -> D -> C -> E -> object
        • 之所以大家误以为是Python2.2的新式类是广度优先算法是因为在计算有重叠的多继承中的结果与真实的优化后的深度优先算法计算出来的结果是相同的。
  • 在"有重叠的多继承"中,使用优化后的深度优先算法算出来后是:A -> B -> D -> object -> C -> D -> object,即A -> B -> C -> D -> object

  • 问题:

    • 无法检测出有问题的继承

    • 有可能还会违背“局部优先”的原则

    • 例如:这样的继承是有问题的,因为A继承了B、D、object ,而A又继承了C、B、D、object,这两部分是包含关系。

      在这里插入图片描述

      按照Python2.2中新式类的改进深度优先算法是无法检测出来问题的,并且还会计算出一个结果给我们:A -> C -> B -> D -> object

      这个图对于“局部优先”原则也是违背的,如图所示A继承B和C,当A尝试去访问一个资源的时候,在A没有的前提下,A应该先选择左侧的B,然后再选择右侧的C,但是结果确实先选择了C后选择了B,但是我们代码写的是class A(B, C) ,所以就违背了局部优先原则。

Python2.3-2.7

  • 因为在Python2.2版本存在无法检测出有问题的继承同时有可能违背局部优先原则,所以在Python2.3开始直到Python2.7结束,对MRO原则进行了优化。

  • MRO原则:

    • 经典类:还是之前的深度优先(从左到右),注意不是优化后的深度优先算法哦!
    • 新式类:就不再采用优化后的深度优先算法了,而是采用C3算法
  • 注意:C3算法只是类似拓扑排序,但是并不是,这里要切记。

    • 拓扑算法:
      • 选择一个入度为0 的顶点并输出之;
      • 从网中删除此顶点及所有出边

Python3.x

  • 在Python3.x版本中只有新式类,没有经典类,所以只有C3算法。
  • 查看MRO的三种方式:
    • 使用inspect模块的getmro(A)方法
    • 打印A.__mro__
    • 打印A.mro()

C3算法的分步推演

# 这里使用 Python 2.7.13
# 这里就不画图了,就是文章开始的“有重叠的多继承链”,也称棱形继承
# 注意这里是经典类,使用的是深度优先算法。
import inspect
class D:pass
class B(D):pass
class C(D):pass
class A(B, C):pass
print inspect.getmro(A) # A -> B -> D -> C
# 这里使用 Python 2.7.13
# 这里就不画图了,就是文章开始的“有重叠的多继承链”,也称棱形继承
# 注意这里是新式类,使用的是C3算法。
import inspect
class D(object):pass
class B(D):pass
class C(D):pass
class A(B, C):pass
print inspect.getmro(A) # A -> B -> C -> D -> object
# 手动计算C3算法
# 这里仍旧使用 Python 2.7.13
# 这里就不画图了,就是文章开始的“有重叠的多继承链”,也称棱形继承
# 注意要继承object后成为新式类"""C3算法如下:
L(object) = [object]
L(本类(父类1, 父类2)) = [本类] + merge(L(父类1), L(父类2), [父类1,父类2])merge算法:1. 如果第一个列表的第一个元素是后续列表的第一个元素,则将这个元素合并到最终的解析列表中,并从当前操作的所有列表中删除。或者后续列表中没有再次出现,则将这个元素合并到最终的解析列表中,并从当前操作的所有列表中删除。2. 如果1中的两个条件都不符合,则跳过此元素,查找下一个列表的第一个元素,重复1的判断规则。3. 如果最终无法把所有元素归并到解析列表,则报错。
"""class D(object):pass# L(D(object)) = [D] + merge(L(object), [object])# L(D(object)) = [D] + merge([object], [object])# 此时merge方法中的参数都是列表了,就可以进行merge算法的处理了# 符合merge算法的条件1,即第一个列表的第一个元素object是后续列表的第一个元素,那么将object从当前merge中所有的列表中删除,并添加到最终的解析列表中# L(D(object)) = [D, object] + merge([], [])# L(D(object)) = [D, object]  # 所以关于D的资源访问顺序就是先D后objectclass B(D):pass# L(B(D)) = [B] + merge(L(D), [D])# L(D)从上面的结果中得知是[D, object]# L(B(D)) = [B] + merge([D, object], [D])# 此时merge方法中的参数都是列表了,就可以进行merge算法的处理了# 符合merge算法的条件1,即第一个列表的第一个元素D是后续列表的第一个元素,那么将D从当前merge中所有的列表中删除,并添加到最终的解析列表中# L(B(D)) = [B, D] + merge([object], [])# 符合merge算法的条件1,虽然object不是后续列表的第一个元素,但是符合后续列表中没有再次出现object,则将这个object元素合并到最终的解析列表中,并从当前操作的所有列表中删除object# L(B(D)) = [B, D, object] + merge([], [])# L(B(D)) = [B, D, object]  # 所以关于B的资源访问顺序就是B -> D -> object
class C(D):pass# L(C(D)) = [C] + merge(L(D), [D])# L(D)从上面的结果中得知是[D, object]# L(C(D)) = [C] + merge([D, object], [D])# 此时merge方法中的参数都是列表了,就可以进行merge算法的处理了# 符合merge算法的条件1,即第一个列表的第一个元素D是后续列表的第一个元素,那么将D从当前merge中所有的列表中删除,并添加到最终的解析列表中# L(C(D)) = [C, D] + merge([object], [])# 符合merge算法的条件1,虽然object不是后续列表的第一个元素,但是符合后续列表中没有再次出现object,则将这个object元素合并到最终的解析列表中,并从当前操作的所有列表中删除object# L(C(D)) = [C, D, object] + merge([], [])# L(C(D)) = [C, D, object]  # 所以关于C的资源访问顺序就是C -> D -> object
class A(B, C):pass# L(A(B, C)) = [A] + merge(L(B), L(C), [B, C])# L(B)从上面的结果中得知是[B, D, object]# L(C)从上面的结果中得知是[C, D, object]# L(A(B, C)) = [A] + merge([B, D, object], [C, D, object], [B, C])# 此时merge方法中的参数都是列表了,就可以进行merge算法的处理了# 符合merge算法的条件1,即第一个列表的第一个元素B是后续列表的第一个元素,那么将B从当前merge中所有的列表中删除,并添加到最终的解析列表中# L(A(B, C)) = [A, B] + merge([D, object], [C, D, object], [C])# 不符合merge算法的条件1,即第一个列表的第一个元素D不是后续列表的第一个元素,并且元素D在第二个列表中存在了,所以merge算法的条件1都不满足,则跳过此元素D,查找下一个列表的第一个元素C,重复1的判断规则,发现C确实是后续列表的首元素,则将这个元素C合并到最终的解析列表中,并从当前操作的所有列表中删除元素C# L(A(B, C)) = [A, B, C] + merge([D, object], [D, object], [])# 符合merge算法的条件1,即第一个列表的第一个元素D是后续列表的第一个元素,则将这个元素D合并到最终的解析列表中,并从当前操作的所有列表中删除元素D# L(A(B, C)) = [A, B, C, D] + merge([object], [object], [])# 符合merge算法的条件1,即第一个列表的第一个元素object是后续列表的第一个元素,那么将object从当前merge中所有的列表中删除,并添加到最终的解析列表中# L(A(B, C)) = [A, B, C, D, object] + merge([], [], [])# L(A(B, C)) = [A, B, C, D, object]  # 所以关于A的资源访问顺序就是A -> B -> C -> D -> object

C3算法检测出有问题的继承

# C3算法能否解决在Python2.2版本中关于新式类的 优化版深度优先算法 存在的问题呢?
# 这里仍旧使用 Python 2.7.13
class D(object):pass# L(D) = [D] + merge(L(object), [object])# L(D) = [D] + merge([object], [object])# L(D) = [D, object] + merge([], [])# L(D) = [D, object]
class B(D):pass# L(B) = [B] + merge(L(D), [D])# L(B) = [B] + merge([D, object], [D])# L(B) = [B, D] + merge([object], [])# L(B) = [B, D, object] + merge([], [])# L(B) = [B, D, object]
class C(B):pass# L(C) = [C] + merge(L(B), [B])# L(C) = [C] + merge([B, D, object], [B])# L(C) = [C, B] + merge([D, object], [])# L(C) = [C, B, D] + merge([object], [])# L(C) = [C, B, D, object] + merge([], [])# L(C) = [C, B, D, object]
class A(B, C): # 抛出TypeError错误pass# L(A) = [A] + merge(L(B), L(C), [B, C])# L(A) = [A] + merge([B, D, object], [C, B, D, object], [B, C])# 此时merge方法中的参数都是列表了,就可以进行merge算法的处理了# 不符合merge算法的条件1,即第一个列表的第一个元素B不是后续列表的第一个元素,并且元素B在第二个列表中存在了,所以merge算法的条件1都不满足,则跳过此元素B,查找下一个列表的第一个元素C,重复1的判断规则,发现C也不是后续列表的第一个元素,且元素C在后续列表中有再次出现,则跳过此元素C,查找下一个列表的第一个元素B,此时merge函数要处理的列表根本无法处理,出现无解情况,说明当前是错误的继承关系,也就是检测出有问题的继承了# 此时如果我们直接运行代码,会报错:无法创建不矛盾的MRO
# TypeError: Cannot create a consistent method resolution
# order (MRO) for bases B, C
# 这里使用的是Python3.x版本
# 当然你也可以使用Python2.3到2.7版本,但是要注意继承自object,这样才能使用新式类。
# 这里就不画图了,就是文章开始的"无重叠的多继承链"
import inspectclass F:pass# L(F) = [F] + merge(L(object), [object])# L(F) = [F] + merge([object], [object])# L(F) = [F, object] + merge([], [])# L(F) = [F, object]class E(F):pass# L(E) = [E] + merge(L(F), [F])# L(E) = [E] + merge([F, object], [F])# L(E) = [E, F] + merge([object], [])# L(E) = [E, F, object] + merge([], [])# L(E) = [E, F, object]class D(F):pass# L(D) = [D] + merge(L(F), [F])# L(D) = [D] + merge([F, object], [F])# L(D) = [D, F] + merge([object], [])# L(D) = [D, F, object] + merge([], [])# L(D) = [D, F, object]class C(E):pass# L(C) = [C] + merge(L(E), [E])# L(C) = [C] + merge([E, F, object], [E])# L(C) = [C, E] + merge([F, object], [])# L(C) = [C, E, F] + merge([object], [])# L(C) = [C, E, F, object] + merge([], [])# L(C) = [C, E, F, object]class B(D):pass# L(B) = [B] + merge(L(D), [D])# L(B) = [B] + merge([D, F, object], [D])# L(B) = [B, D] + merge([F, object], [])# L(B) = [B, D, F] + merge([object], [])# L(B) = [B, D, F, object] + merge([], [])# L(B) = [B, D, F, object]class A(B, C):pass# L(A) = [A] + merge(L(B), L(C), [B, C])# L(A) = [A] + merge([B, D, F, object], [C, E, F, object], [B, C])# L(A) = [A, B] + merge([D, F, object], [C, E, F, object], [C])# L(A) = [A, B, D] + merge([F, object], [C, E, F, object], [C])# L(A) = [A, B, D, C] + merge([F, object], [E, F, object], [])# L(A) = [A, B, D, C, E] + merge([F, object], [F, object], [])# L(A) = [A, B, D, C, E, F] + merge([object], [object], [])# L(A) = [A, B, D, C, E, F, object] + merge([], [], [])# L(A) = [A, B, D, C, E, F, object]print(inspect.getmro(A))  # A -> B -> D -> C -> E -> F -> object

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/193827.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】tensorflow学习的个人纪录(3)

sess tf.Session()actor Actor(sess, n_featuresN_S, lrLR_A, action_bound[-A_BOUND, A_BOUND])步进:

HDMI之数据岛

概述 发送端在发送视频信号之前,将多媒体信息通过数据岛传输给接收端。接收端通过数据岛信息获取当前分辨率(VIC),编码信息(RGB/YCR等),色彩空间,位深等等。然后对应将视频信息解码。与此同时,多余的带宽用于传输音频信息等。本文通过具体的包信息(从实验室仪器拍照…

智能优化算法应用:基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于模拟退火算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.模拟退火算法4.实验参数设定5.算法结果6.参考…

4_企业架构双点服务器HA

企业架构双点服务器HA 学习目标和内容 1、能够描述高可用HA的作用 2、能够理解VIP的切换 3、能够描述keepalived作用 4、能够理解主master和备backup服务器关系 5、能够实现主备服务器高可用配置 6、能够实现模拟业务宕机服务切换 一、背景描述及其方案设计 1、业务背景描述 时…

JavaSE学习路线及经验所谈

前言 一.学习框架二.学习经验 相信很多小白刚开始学习Java时,都是靠自己在网上搜集资料,并没有明确规划,不知道要学习什么内容,也不知道学习的重点是什么,那么这篇文章会给你一个大致的指引,当然也可以作为…

网络之路27:IRF设备堆叠

正文共:3210 字 34 图,预估阅读时间:5 分钟 目录 网络之路第一章:Windows系统中的网络 0、序言 1、Windows系统中的网络1.1、桌面中的网卡1.2、命令行中的网卡1.3、路由表1.4、家用路由器 网络之路第二章:认识企业设备…

每日一题:LeetCode-209. 长度最小的子数组(滑动窗口)

每日一题系列(day 11) 前言: 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 &#x1f50e…

【BLE基础知识】--Slave latency设置流程及空中包解析

1、Slave latency基本概念 当BLE从设备对耗电量要求较高时,若需要节省耗电量,则可以通过设置Slave Latency参数来减少BLE从设备的耗电。 Slave Latency:允许Slave(从设备)在没有数据要发的情况下,跳过一定…

如何在线给官网搭建一个帮助文档?

在数字化时代,帮助文档已成为官网不可或缺的一部分。然而,传统的帮助文档往往只是简单地罗列问题和答案,缺乏互动性和用户体验。那么,如何在线给官网搭建一个富有创意且用户友好的帮助文档呢? | 一、打造沉浸式体验 传…

C/C++学生选课/排课系统[2023-12-3]

问题描述:根据我校自动化专业的部分必修及选修课信 息,设计一个学生选课/排课系统。 基本要求: 1、从文件读入课程信息; 2、从键盘输入拟添加的选修课信息; 3、删除已选的选修课(1门或多门) ; 4、输出已…

手写实现一个动态代理框架

手写实现一个动态代理框架 什么是代理模式什么是动态代理动态代理中的编译、类加载与对象实例化手写实现一个动态代理框架实现细节DynamicProxyHandlerProxy生成代码写入代码到磁盘文件调用编译器进行编译调用类加载器进行类加载反射实例化删除前面生成的java文件和class文件 C…

全网关键词采集,免费关键词采集软件使用方法

网站的SEO优化已经成为企业提升在线可见性的不二选择。而关键词的选择和使用则是SEO优化的核心。本文将专心分享关键词采集的正确用法,助您在SEO的道路上掌握正确的方向。 关键词采集:SEO的基础 让我们明确关键词采集的重要性。在搜索引擎的世界里&…

Python生产者消费者模型

额滴名片儿 🎈 博主:一只程序猿子 🎈 博客主页:一只程序猿子 博客主页 🎈 个人介绍:爱好(bushi)编程! 🎈 创作不易:如喜欢麻烦您点个👍或者点个⭐&#xff01…

【蓝桥杯选拔赛真题73】Scratch烟花特效 少儿编程scratch图形化编程 蓝桥杯创意编程选拔赛真题解析

目录 scratch烟花特效 一、题目要求 编程实现 二、案例分析 1、角色分析

组件的props属性

目录 1:使用props的作用: 2:props自定义属性的用法: 3:集合v-bind使用自定义属性: 4:props自定义属性是只读的: 5:default默认值: 6:type值类…

(Python) 特殊变量

整体 内置模块 name 用到的模块 对象 函数

掌握视频剪辑技巧:批量置入视频封面,提升视频品质

在当今数字化时代,视频已成为生活的重要组成部分。无论是观看电影、电视剧、综艺节目,还是分享个人生活、工作成果,视频都以其独特的魅力吸引着大众的视线。视频封面是视频内容的缩影,是观众对视频的第一印象。一个好的封面能吸引…

ArrayList 与 顺序表 (附洗牌算法)!

曾经我也是一枚学霸,直到有一天想去学渣的世界看看,结果就找不到回去的路了。 目录 1. 线性表 2.顺序表 2.1 接口的实现 3. ArrayList简介 4. ArrayList使用 4.1 ArrayList的构造 4.2 ArrayList常见操作 4.3 ArrayList的遍历 4.4 ArrayList的扩…

Flutter PK jetpack compose区别和选择

Flutter诞生于Chrome团队,是一帮做Web的开发做的跨平台框架,从最开始的设计初衷,就是指向了跨平台这条路,而Compose,则是诞生于Android团队,是为了解决当前View的架构体系不能再继续适应申明式编程的范式而…

使用drawio图表,在团队中,做计划,设计和跟踪项目

使用drawio图表,在团队中,做计划,设计和跟踪项目 drawio是一款强大的图表绘制软件,支持在线云端版本以及windows, macOS, linux安装版。 如果想在线直接使用,则直接输入网址draw.io或者使用drawon(桌案), drawon.cn内部…