python中的方法解析顺序

时间:2019-07-09 04:40:40

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

我是python的新手。我正在使用python 2.7。我正在通过一个小的代码段进行方法顺序解析,如下所示:

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的父母。谢谢。

3 个答案:

答案 0 :(得分:3)

我们将仅以经典的python类为例来理解method resolution order。请记住,由于Python 3不支持经典的python类,因此MRO的概念仅适用于Python 2.7。

请考虑以下示例:

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)

答案 1 :(得分:0)

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

class D(C,B):
    pass

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

答案 2 :(得分:0)

如果您停止考虑它,那只是直观的工作方式。到目前为止,This article看起来像是一个考古发现,仍然是对Python方法解析顺序算法的权威描述和推理。

但是,尽管其中有技术细节,但您的两个示例中发生了什么:

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

在第二个层次结构D,B,C,A,E中,B优先于C,再次表明应使用A.attr。但是,这次,A自己的属性并未被层次结构中的另一个类遮蔽-而是C.attr来自另一个“世系”-因此该语言选择了它遇到的第一个。

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

  

[一个类别] C的线性化是C的和加上C的合并   父母和父母名单的线性化。   ...   [给定类C(B1,...,BN):],取第一个列表的开头,即L [B1] [0] [基B1到对象的线性化(aka mro)-开头为B1- ];如果这个头不在   其他列表[其他碱基的线性化列表]的尾部,然后将其添加到线性化中   C,然后将其从合并列表中删除,否则请查看   如果这是一个不错的头,请选择下一个列表的头并接受。然后重复   直到所有班级被删除或不可能   找到好头。在这种情况下,无法构建   合并后,Python 2.3(及后续版本)将拒绝创建类C并引发   

引入第二个示例,您有 D(B, C)-B和C的线性化分别为:[B, A, object][C, E, object],D的线性化以“ B”开头,检查是否为“ B” 在任何其他列表的尾部(并且不在[C,E,object]上),则采用B。其余列表为[A, object][C, E, object]-算法然后选择A不在另一个列表中,然后将A附加到D的mro。然后选择object在另一个列表上。因此,该算法使第一个列表保持不变,并采用C,E和最后一个对象进行D, B, A, C, E, object线性化。

在您的第一个示例中,当算法检查[B, A, object]时,两个碱基的线性化分别为[C, A, object]A,它位于第二个列表-因此,C比第二个列表中的A优先选择-最终线性化为D, B, C, A, object