【问题标题】:Method resolution order in pythonpython中的方法解析顺序
【发布时间】:2019-07-09 04:40:40
【问题描述】:

我是 python 新手。我正在使用python 2.7。我正在使用一个小的 sn-p 进行方法顺序解析,如下所示:

class A(object):
    attr = 'A'

class B(A):
    pass

class C(A):
    attr = 'C'

class D(B,C):
    pass

x = D()
print x.attr

分辨率的顺序是x,D,B,C,A,因此输出是C。按照上面的例子,我对代码做了一点改动。

class A(object):
    attr = 'A'

class E(object):
    attr = 'E'

class B(A):
    pass

class C(E):
    attr = 'C'

class D(B,C):
    pass

x = D()
print x.attr

按照我之前的示例,我希望顺序是 x、D、B、C、A、E。令我惊讶的是,输出是“A”。因此,我对新式课程的解决顺序感到困惑。有人可以澄清在 C 课之前何时访问 B 的父母。谢谢。

【问题讨论】:

  • 您可以查看 MRO,例如:print(D.__mro__)
  • 如果您是 Python 新手,请使用 Python3 pythonclock.org
  • Python 2 “新”样式类在 Python 3 中的工作方式相同。但此时尝试 Python 2 几乎是浪费时间。 Python2 几乎走到了生命的尽头——只需使用 Python 3 进行研究和实验(当然还有项目)。

标签: python method-resolution-order python-mro


【解决方案1】:

如果你停下来想一想,这只是直观的工作方式。 This article,现在看来只是考古发现,仍然是 Python 方法解析顺序算法的权威描述和推理。

但是,尽管其中有技术细节,但在您的两个示例中发生的情况是:

在第一个D,B,C,A中,通过B的路径表示应该使用A的属性。但是As 属性本身被C 中的属性所遮蔽——也就是说,C 中的声明覆盖了A 中声明的attr。因此,它是使用的。

在第二个层次结构中,D,B,C,A,E,B 先于 C,再次表明应该使用 A.attr。然而,这一次,A 自己的属性并没有被层次结构中的另一个类所掩盖——而是 C.attr 来自另一个“血统”——所以语言选择了它遇到的第一个。

这是对正在发生的事情的“简单英语描述”。上面链接的权威文章为此制定了正式规则:

[a class] C 的线性化是 C 的总和加上 父母的线性化和父母的名单。 ... [给定类 C(B1, ..., BN):],取第一个列表的头部,即 L[B1][0] [Base B1 到 Object 的线性化(又名 mro) - 头部是 B1 - ];如果这个头不在 任何其他列表的尾部 [其他碱基的线性化列表] ,然后将其添加到线性化 的 C 并将其从合并中的列表中删除,否则请查看 下一个列表的头并拿走它,如果它是一个好头。然后重复 操作直到所有类都被删除或不可能 找到好人头。在这种情况下,无法构建 合并,Python 2.3 [和后续版本] 将拒绝创建 C 类并引发 例外。

引入你的第二个例子,你有 D(B, C) - B 和 C 的线性化是:[B, A, object][C, E, object] 并且 D 的线性化从“B”开始,检查它不是 在任何其他列表的尾部(并且它不在 [C, E, object] 上),然后采用 B。剩下的列表是[A, object][C, E, object] - 然后算法选择A 它不在另一个列表中,然后A 被附加到D 的mro。然后它选择object。它在另一个列表中。所以算法保持第一个列表不变,并采用 C、E 和最后的对象,用于D, B, A, C, E, object 线性化。

在您的第一个示例中,当算法检查 A 时,两个碱基的线性化分别是 [B, A, object][C, A, object],它 位于第二个列表的尾部 - 所以,从第二个列表中选择C,而不是A - 最终的线性化是D, B, C, A, object

【讨论】:

  • 这应该可以解决问题。很好的解释。
【解决方案2】:

我们将仅通过经典python类的示例来理解method resolution order。请记住,MRO 的这个概念只适用于 Python 2.7,因为 Python 3 不支持经典的 Python 类。

考虑以下示例:

class A():
    #pass
    def who_am_i(self):
        print("I am a A")    


class B(A):
    #pass
    def who_am_i(self):
        print("I am a B")


class C(A):
    #pass
    def who_am_i(self):
        print("I am a C")

class D(B,C):
    #pass
    def who_am_i(self):
       print("I am a D")

d1 = D()
d1.who_am_i()

这是上面程序的输出:

I am a D

从输出中,我们可以看到 D 类的方法按预期首先被调用。这是因为 D 类位于它继承 B 类和 C 类的类的最低层次。现在,当 D 类的实例中被调用的方法在类中不可用时,会首先解决哪个类的方法的问题D 本身和解释器必须在 D 的两个父类(即B 级和 C 级可供选择。那么,让我们从 D 类中删除这个方法,看看解释器做了什么。

class A():
    #pass
    def who_am_i(self):
        print("I am a A")    


class B(A):
    #pass
    def who_am_i(self):
        print("I am a B")


class C(A):
    #pass
    def who_am_i(self):
        print("I am a C")

class D(B,C):
    pass    

d1 = D()
d1.who_am_i()

这是上面程序的输出:

I am a B

尽管 B 和 C 都有所需的方法,但解释器调用的是 B 类的方法,而不是 C 类的方法。

您可以在此处找到更多示例。 Method Resolution Order in Python (MRO)

【讨论】:

    【解决方案3】:

    是因为顺序,所以如果D是:

    class D(C,B):
        pass
    

    它将输出C,因为它获取了继承的第一个类的attr属性。

    【讨论】:

      猜你喜欢
      • 2021-03-05
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多