标签归档:method-resolution-order

“ mro()”有什么作用?

问题:“ mro()”有什么作用?

django.utils.functional.py

for t in type(res).mro():  # <----- this
    if t in self.__dispatch:
        return self.__dispatch[t][funcname](res, *args, **kw)

我不明白mro()。它是做什么的,“ mro”是什么意思?

In django.utils.functional.py:

for t in type(res).mro():  # <----- this
    if t in self.__dispatch:
        return self.__dispatch[t][funcname](res, *args, **kw)

I don’t understand mro(). What does it do and what does “mro” mean?


回答 0

跟着…:

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

只要我们具有单一继承,__mro__它就只是该类的元组:类,其基础,其基础的基础,依此类推object(当然,仅适用于新型类)。

现在,具有多重继承…:

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

…您还可以确保,在中__mro__,没有类被重复,并且没有类在其祖先之后出现,除了首先进入相同多重继承级别的类(如本例中的B和C)之外,__mro__左到右。

从类的实例(而不是方法)获得的每个属性在概念上都会沿进行查找__mro__,因此,如果祖先中有多个类定义了该名称,则可以告诉您在哪里可以找到该属性-在第一个类中在__mro__定义该名称。

Follow along…:

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

As long as we have single inheritance, __mro__ is just the tuple of: the class, its base, its base’s base, and so on up to object (only works for new-style classes of course).

Now, with multiple inheritance…:

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

…you also get the assurance that, in __mro__, no class is duplicated, and no class comes after its ancestors, save that classes that first enter at the same level of multiple inheritance (like B and C in this example) are in the __mro__ left to right.

Every attribute you get on a class’s instance, not just methods, is conceptually looked up along the __mro__, so, if more than one class among the ancestors defines that name, this tells you where the attribute will be found — in the first class in the __mro__ that defines that name.


回答 1

mro()代表方法解析顺序。它以搜索方法的顺序返回类派生的类型的列表。

mro()__mro__仅适用于新样式类。在python 3中,它们可以正常工作。但是在python 2中,这些类需要从继承object

mro() stands for Method Resolution Order. It returns a list of types the class is derived from, in the order they are searched for methods.

mro() or __mro__ works only on new style classes. In python 3, they work without any issues. But in python 2 those classes need to inherit from object.


回答 2

这也许会显示解决的顺序。

class A(object):
    def dothis(self):
        print('I am from A class')

class B(A):
    pass

class C(object):
    def dothis(self):
        print('I am from C class')

class D(B, C):
    pass

d_instance= D()
d_instance.dothis()
print(D.mro())

和响应将是

I am from A class
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]

该规则是深度优先的,在这种情况下,其含义是D,B,A,C。

在搜索继承的类时,Python通常使用深度优先的顺序,但是当两个类从同一个类继承时,Python将从mro中删除对该类的首次提及。

This would perhaps show the order of resolution.

class A(object):
    def dothis(self):
        print('I am from A class')

class B(A):
    pass

class C(object):
    def dothis(self):
        print('I am from C class')

class D(B, C):
    pass

d_instance= D()
d_instance.dothis()
print(D.mro())

and response would be

I am from A class
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]

The rule is depth-first, which in this case would mean D, B, A, C.

Python normally uses a depth-first order when searching inheriting classes, but when two classes inherit from the same class, Python removes the first mention of that class from mro.


回答 3

钻石继承的解决顺序将有所不同。

class A(object):
    def dothis(self):
        print('I am from A class')


class B1(A):
    def dothis(self):
        print('I am from B1 class')
    # pass


class B2(object):
    def dothis(self):
        print('I am from B2 class')
    # pass


class B3(A):
    def dothis(self):
        print('I am from B3 class')


# Diamond inheritance
class D1(B1, B3):
    pass


class D2(B1, B2):
    pass


d1_instance = D1()
d1_instance.dothis()
# I am from B1 class
print(D1.__mro__)
# (<class '__main__.D1'>, <class '__main__.B1'>, <class '__main__.B3'>, <class '__main__.A'>, <class 'object'>)


d2_instance = D2()
d2_instance.dothis()
# I am from B1 class
print(D2.__mro__)
# (<class '__main__.D2'>, <class '__main__.B1'>, <class '__main__.A'>, <class '__main__.B2'>, <class 'object'>)

Order of resolution will be different in diamond inheritance.

class A(object):
    def dothis(self):
        print('I am from A class')


class B1(A):
    def dothis(self):
        print('I am from B1 class')
    # pass


class B2(object):
    def dothis(self):
        print('I am from B2 class')
    # pass


class B3(A):
    def dothis(self):
        print('I am from B3 class')


# Diamond inheritance
class D1(B1, B3):
    pass


class D2(B1, B2):
    pass


d1_instance = D1()
d1_instance.dothis()
# I am from B1 class
print(D1.__mro__)
# (<class '__main__.D1'>, <class '__main__.B1'>, <class '__main__.B3'>, <class '__main__.A'>, <class 'object'>)


d2_instance = D2()
d2_instance.dothis()
# I am from B1 class
print(D2.__mro__)
# (<class '__main__.D2'>, <class '__main__.B1'>, <class '__main__.A'>, <class '__main__.B2'>, <class 'object'>)

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

问题:新型类中的方法解析顺序(MRO)?

在《Nutshell的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。

In the book Python in a Nutshell (2nd Edition) there is an example which uses
old style classes to demonstrate how methods are resolved in classic resolution order and
how is it different with the new order.

I tried the same example by rewriting the example in new style but the result is no different than what was obtained with old style classes. The python version I am using to run the example is 2.5.2. Below is the example:

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__  

The call instance.amethod() prints Base1, but as per my understanding of the MRO with new style of classes the output should have been Base3. The call Derived.__mro__ prints:

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

I am not sure if my understanding of MRO with new style classes is incorrect or that I am doing a silly mistake which I am not able to detect. Please help me in better understanding of MRO.


回答 0

在“天真”的深度优先方法中,同一祖先类出现多次时,旧类与新类的解析顺序之间的关键区别就出现了:例如,考虑“钻石继承”情况:

>>> 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被迫来到分辨率顺序只有一次,毕竟它的子类,从而使覆盖(即尺寸材料的的覆盖x)实际上有效地工作。

这是应避免使用旧式类的原因之一:“菱形”模式的多重继承对它们而言并不明智,而对新式则可行。

The crucial difference between resolution order for legacy vs new-style classes comes when the same ancestor class occurs more than once in the “naive”, depth-first approach — e.g., consider a “diamond inheritance” case:

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

here, legacy-style, the resolution order is D – B – A – C – A : so when looking up D.x, A is the first base in resolution order to solve it, thereby hiding the definition in C. While:

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

here, new-style, the order is:

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

with A forced to come in resolution order only once and after all of its subclasses, so that overrides (i.e., C’s override of member x) actually work sensibly.

It’s one of the reasons that old-style classes should be avoided: multiple inheritance with “diamond-like” patterns just doesn’t work sensibly with them, while it does with new-style.


回答 1

实际上,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^
#     / \
#    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^
#       /  |  \
#     /    |    \
#   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^
#       /  |  \
#     /    |    \
#   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^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#               |
#               |
#               H

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

# Level {D C B}
#
#      A^
#     /| \
#    / |  \
#   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()

Python’s method resolution order is actually more complex than just understanding the diamond pattern. To really understand it, take a look at C3 linearization. I’ve found it really helps to use print statements when extending methods to track the order. For example, what do you think the output of this pattern would be? (Note: the ‘X’ is suppose to be two crossing edges, not a node and ^ signifies methods that call 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^
#     / \
#    B^  C^
#   /| X
# D^ E^ F^
#  \ | /
#    G

Did you get A B D C E F G?

x = A()
x.m()

After a lot of trial an error, I came up with an informal graph theory interpretation of C3 linearization as follows: (Someone please let me know if this is wrong.)

Consider this example:

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^
#       /  |  \
#     /    |    \
#   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^
#       /  |  \
#     /    |    \
#   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^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#               |
#               |
#               H

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

# Level {D C B}
#
#      A^
#     /| \
#    / |  \
#   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()

回答 2

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

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

阅读此说明以获取更多信息。

The result you get is correct. Try changing base class of Base3 to Base1 and compare with the same hierarchy for classic classes:

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()

Now it outputs:

Base3
Base1

Read this explanation for more information.


回答 3

您会看到这种现象,因为方法解析是深度优先的,而不是广度优先的。Dervied的继承看起来像

         Base2 -> Base1
        /
Derived - Base3

所以 instance.amethod()

  1. 检查Base2,找不到方法。
  2. 确认Base2已从Base1继承,并检查Base1。Base1有一个amethod,因此被调用。

这反映在中Derived.__mro__Derived.__mro__找到要查找的方法时,只需简单地迭代并停止。

You’re seeing that behavior because method resolution is depth-first, not breadth-first. Dervied’s inheritance looks like

         Base2 -> Base1
        /
Derived - Base3

So instance.amethod()

  1. Checks Base2, doesn’t find amethod.
  2. Sees that Base2 has inherited from Base1, and checks Base1. Base1 has a amethod, so it gets called.

This is reflected in Derived.__mro__. Simply iterate over Derived.__mro__ and stop when you find the method being looked for.