新式类中的方法解析顺序(MRO)?

2025-01-14 08:50:00
admin
原创
106
摘要:问题描述:在《Python 简介(第 2 版)》一书中,有一个例子使用 旧式类来演示如何按照经典解析顺序解析方法,以及 与新顺序有何不同。我尝试用新样式重写该示例,但结果与使用旧样式类获得的结果没有什么不同。我用来运行示例的 Python 版本是2.5.2。以下是示例:class Base1(object...

问题描述:

在《Python 简介(第 2 版)》一书中,有一个例子使用

旧式类来演示如何按照经典解析顺序解析方法,以及

与新顺序有何不同。

我尝试用新样式重写该示例,但结果与使用旧样式类获得的结果没有什么不同。我用来运行示例的 Python 版本是2.5.2。以下是示例:

class Base1(object):  
    def amethod(self): print "Base1"  

class Base2(Base1):  
    pass

class Base3(object):  
    def amethod(self): print "Base3"

class Derived(Base2,Base3):  
    pass

instance = Derived()  
instance.amethod()  
print Derived.__mro__  

调用instance.amethod()会打印Base1,但根据我对新式类的 MRO 的理解,输出应该是Base3。调用会Derived.__mro__打印:

(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)

我不确定我对新式类 MRO 的理解是否不正确,或者我犯了一个我无法发现的愚蠢错误。请帮助我更好地理解 MRO。


解决方案 1:

遗留类和新式类的解析顺序之间的关键区别在于,当同一个祖先类在“朴素”的深度优先方法中出现多次时 - 例如,考虑“菱形继承”的情况:

>>> class A: x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'a'

这里,按照传统风格,解析顺序为 D - B - A - C - A :因此,当查找 Dx 时,A 是按照解析顺序第一个解决它的基数,从而隐藏了 C 中的定义。而:

>>> class A(object): x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'c'
>>> 

这里,新风格的顺序是:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, 
    <class '__main__.A'>, <type 'object'>)

强制A按照解析顺序仅出现一次,并且在其所有子类之后,以便覆盖(即,C 的成员覆盖x)实际上可以合理地工作。

这是应该避免使用旧式类的原因之一:具有“菱形”模式的多重继承对它们不起作用,但对新式类却起作用。

解决方案 2:

Python 的方法解析顺序实际上比仅仅理解菱形模式更复杂。要真正理解它,请查看C3 线性化。我发现在扩展方法以跟踪顺序时使用打印语句确实很有帮助。例如,您认为此模式的输出是什么?(注意:'X' 应该是两个交叉边,而不是节点,^ 表示调用 super() 的方法)

class G():
    def m(self):
        print("G")

class F(G):
    def m(self):
        print("F")
        super().m()

class E(G):
    def m(self):
        print("E")
        super().m()

class D(G):
    def m(self):
        print("D")
        super().m()

class C(E):
    def m(self):
        print("C")
        super().m()

class B(D, E, F):
    def m(self):
        print("B")
        super().m()

class A(B, C):
    def m(self):
        print("A")
        super().m()


#      A^
#     / \n#    B^  C^
#   /| X
# D^ E^ F^
#   | /
#    G

您得到 ABDCEFG 了吗?

x = A()
x.m()

经过多次反复试验后,我得出了 C3 线性化的非正式图论解释,如下所示:(如果这是错误的,请告诉我。)

考虑这个例子:

class I(G):
    def m(self):
        print("I")
        super().m()

class H():
    def m(self):
        print("H")

class G(H):
    def m(self):
        print("G")
        super().m()

class F(H):
    def m(self):
        print("F")
        super().m()

class E(H):
    def m(self):
        print("E")
        super().m()

class D(F):
    def m(self):
        print("D")
        super().m()

class C(E, F, G):
    def m(self):
        print("C")
        super().m()

class B():
    def m(self):
        print("B")
        super().m()

class A(B, C, D):
    def m(self):
        print("A")
        super().m()

# Algorithm:

# 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and
#    keeping the correct left to right order. (I've marked methods that call super with ^)

#          A^
#       /  |  \n#     /    |    \n#   B^     C^    D^  I^
#        / |   /   /
#       /  |  X    /   
#      /   |/    /     
#    E^    F^   G^
#         |    /
#         |  / 
#          H
# (In this example, A is a child of B, so imagine an edge going FROM A TO B)

# 2. Remove all classes that aren't eventually inherited by A

#          A^
#       /  |  \n#     /    |    \n#   B^     C^    D^
#        / |   /  
#       /  |  X    
#      /   |/   
#    E^    F^   G^
#         |    /
#         |  / 
#          H

# 3. For each level of the graph from bottom to top
#       For each node in the level from right to left
#           Remove all of the edges coming into the node except for the right-most one
#           Remove all of the edges going out of the node except for the left-most one

# Level {H}
#
#          A^
#       /  |  \n#     /    |    \n#   B^     C^    D^
#        / |   /  
#       /  |  X    
#      /   |/   
#    E^    F^   G^
#               |
#               |
#               H

# Level {G F E}
#
#         A^
#       / |  \n#     /   |    \n#   B^    C^   D^
#         |  /  
#         |  X    
#         | | \n#         E^F^ G^
#              |
#              |
#              H

# Level {D C B}
#
#      A^
#     /| \n#    / |  \n#   B^ C^ D^
#      |  |  
#      |  |    
#      |  |  
#      E^ F^ G^
#            |
#            |
#            H

# Level {A}
#
#   A^
#   |
#   |
#   B^  C^  D^
#       |   |
#       |   |
#       |   |
#       E^  F^  G^
#               |
#               |
#               H

# The resolution order can now be determined by reading from top to bottom, left to right.  A B C E D F G H

x = A()
x.m()

解决方案 3:

得到的结果是正确的。尝试将 的基类更改Base3Base1,并与经典类的相同层次结构进行比较:

class Base1(object):
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()


class Base1:
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()

现在输出:

Base3
Base1

阅读此解释以获取更多信息。

解决方案 4:

您之所以看到这种行为,是因为方法解析是深度优先,而不是广度优先。Dervied 的继承如下所示

         Base2 -> Base1
        /
Derived - Base3

所以instance.amethod()

  1. 检查 Base2,没有找到方法。

  2. 看到 Base2 继承自 Base1,并检查 Base1。Base1 有一个amethod,因此被调用。

这反映在 中Derived.__mro__。只需迭代Derived.__mro__并在找到所寻找的方法时停止。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用