Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 586|回复: 0

python---方法解析顺序MRO(Method Resolution Order)<以及解决类中super方法>

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-9 15:03:58 | 显示全部楼层 |阅读模式

    MRO了解:

    对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置。
    而搜索的顺序就是所谓的「方法解析顺序」(Method Resolution Order,或MRO)。
    对于只支持单继承的语言来说,MRO 一般比较简单;而对于 Python 这种支持多继承的语言来说,MRO 就复杂很多。

    而具体讨论MRO,我们需要针对不同python版本中的MRO进行解析

    经典类:DFS深度优先搜索(Python2.2以前的版本)
    新式类:BFS广度优先搜索(Python2.2中提出,在与经典类共存的情况下,是否继承object是他们的区分方式)
    新式类C3算法:Python2.3提出(也是现在Python3唯一支持的方式)

    对于下面讨论的类的多重继承:我们讨论两种情况。

    一:经典类(深度优先搜索)

     在经典类中,没有__mro__属性可以去查看MRO的顺序,但是,可以使用inspect模块中getmro方法

    import inspect
    inspect.getmro(类名)

    (一)正常继承模式

     

    在正常继承模式下,不会引起任何问题

    (二)交叉继承模式

     

    缺点:C类原本是D的子类,若是在C中对D的某个方法进行了重载(B类没有进行重载),那么我们在A中所希望使用的是C中的重载方法,

    但是由于查找顺序是A->B->D->C,所以对于在C类中的重载方法,会在结果D时被过滤(因为在D中查找到该方法就停止了),导致永远无法访问到C中的重载方法

    import inspect
    
    class D:
        def foo(self):
            print("D.foo")
    
    class C(D):
        def foo(self):
            print("C.foo")
    
    class B(D):
        pass
    
    class A(B,C):
        pass
    
    print(inspect.getmro(A))    #A->B->D->C
    obj = A()
    obj.foo()   #D.foo
    代码演示

    二:新式类(广度优先搜索) 

    (一)正常继承方式

    缺点:B继承于D,若是D中实现了某个方法,B可以去调用他,但是C中也自定义了一个同名方法。那么B会获取到C中的方法就结束了。这可不是我们所希望出现的

    class E(object):
        pass
    
    class D(object):
        def foo(self):
            print("D.foo")
    
    class C(E):
        def foo(self):
            print("C.foo")
    
    class B(D):
        pass
    
    class A(B,C):
        pass
    
    print(A.__mro__)    #A->B->C->D->E
    obj = A()
    obj.foo()   #C.foo
    代码演示

    (二)交叉继承方式

     

    在交叉继承的方式下,不会出现任何问题

    三:新式类(C3算法实现:看起来就是将上面两者的优点结合了。所以在Python3中全部都是新式类,不需要object继承)

     (一)正常继承方式

     (二)交叉继承方式

     在python3中这种MRO方法是唯一使用的。

     推文:你真的理解Python中MRO算法吗?

    四:C3算法了解

     推文:C3算法了解

     推文:C3算法了解(这个更加详细)

    但是上面两个对于MRO的计算方法都有错误处,不过其中第二篇“C3算法了解”的评论给出了详细的解法。下面我也写出这两篇文章中的具体解法

    注意:我们把类 C 的线性化(MRO)记为 L[C] = [C1, C2,…,CN]。其中 C1 称为 L[C] 的头其余元素 [C2,…,CN] 称为尾
    L[object] = [object]
    L[C(B1,B2,...,B(N-1),BN)] = [C] + merge(L[B1]+L[B2]+...+L[B(N-1)]+L[BN], [B1,B2,...,B(N-1),BN]) #这种解法是正确的
    其他地方可以看上面两篇推文即可(第二篇更加详细)

    步骤:

    1.检查第一个列表的头元素(如 L[B1] 的头),记作 H。
    
    2.若 H 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 H,继续该步骤。(重点)
    3.重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。

     

    (一)推文一:案例推导(其中o代表object类)

    解题步骤:

    (二)推文二:案例推导

     

    解题步骤:

    可能你发现这种解法,和两篇推文中的答案一致。但是你向下看,在super方法中提到一个错误案例,再去使用这种方法和推文中的方法,就知道该如何使用了。



     

    下面讨论__init__和super()方法的关系

    一:在单继承中super方法和__init__使用时功能上基本是无差别的

    class A(object):
        def __init__(self):
            print("A.__init__.start")
            print("A.__init__.end")
    
    class B(A):
        def __init__(self):
            print("B.__init__.start")
            super(B, self).__init__()
            print("B.__init__.end")
    
    class C(A):
        def __init__(self):
            print("B.__init__.start")
            A.__init__(self)
            print("B.__init__.end")
    
    b = B()
    # B.__init__.start
    # A.__init__.start
    # A.__init__.end
    # B.__init__.end
    
    c = C()
    # B.__init__.start
    # A.__init__.start
    # A.__init__.end
    # B.__init__.end

    二:super方法只在新式类中适用

    >>> class A:
    ...     def __init__(self):
    ...         print("A.__init__.start")
    ...         print("A.__init__.end")
    ...
    >>> class B:
    ...     def __init__(self):
    ...         print("B.__init__.start")
    ...         super(B, self).__init__()
    ...         print("B.__init__.end")
    ...
    >>> class C(A):
    ...     def __init__(self):
    ...         print("B.__init__.start")
    ...         A.__init__(self)
    ...         print("B.__init__.end")
    ...
    >>> b = B()
    B.__init__.start
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in __init__
    TypeError: must be type, not classobj

    三:注意super()不是父类,而是执行MRO顺序中的下一个类!!

    class E(object):
        def __init__(self):
            print("E")
    
    class D(object):
        def __init__(self):
            print("D")
            super(D, self).__init__()
    
    class C(E):
        def __init__(self):
            print("C")
            super(C, self).__init__()
    
    class B(D):
        def __init__(self):
            print("B")
            super(B, self).__init__()
    
    class A(B,C):
        def __init__(self):
            print("A")
            super(A, self).__init__()
    
    print(A.__mro__) #A->B->D->C->E
    obj = A()   #ABDCE

    由最后输出可以知道,super是指向MRO顺序中自己类的下一个类。

    def super(class_name, self):
        mro = self.__class__.mro() #获取mro的列表
        return mro[mro.index(class_name) + 1]  #获取自己的索引号,去返回下一个类

    四:super()可以避免重复调用

    (一)__init__方法可能导致被执行多次

    class A(object):
        def __init__(self):
            print("A.__init__")
    
    class B(A):
        def __init__(self):
            print("B.__init__")
            A.__init__(self)
    
    class C(B,A):
        def __init__(self):
            print("C.__init__")
            A.__init__(self)
            B.__init__(self)
    
    c = C()
    C.__init__
    A.__init__
    B.__init__
    A.__init__  #出现重复调用

    注意:

    class C(A,B):  #会因为无法创建MRO而出错
        def __init__(self):
            print("C.__init__")
            A.__init__(self)
            B.__init__(self)
    
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases A, B

    (二)使用super方法可以避免重复调用

    class A(object):
        def __init__(self):
            print("A.__init__")
    
    class B(A):
        def __init__(self):
            print("B.__init__")
            super(B, self).__init__()
    
    class C(B,A):
        def __init__(self):
            print("C.__init__")
            super(C, self).__init__()
    
    c = C()
    C.__init__
    B.__init__
    A.__init__

    同样:

    class C(A,B):  #也是因为无法生成MRO顺序出错
        def __init__(self):
            print("C.__init__")
            super(C, self).__init__()
    
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases A, B

    详细解决可以在推文:C3算法了解(2)中看到。下面也会写产生的原因(在生成MRO顺序时出错)

    正确继承下的解法:

     

    解法C3算法:

    错误继承的原因:

     

    C3算法解题:

    推文:https://www.zhihu.com/question/20040039

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 16:04 , Processed in 0.069968 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表