新型类中的方法解析顺序(MRO)?
-
13-09-2019 - |
题
在书里 Python 简述(第二版) 有一个例子使用
旧式类演示如何以经典解析顺序解析方法,以及
与新订单有何不同?
我通过以新样式重写示例来尝试相同的示例,但结果与旧样式类获得的结果没有什么不同。我用来运行示例的 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。
解决方案
对于传统VS新式的类分辨率秩序之间的关键区别来当同一个祖先类中发生比“天真”,深度优先的方式再一次 - 例如,考虑一个“菱形继承”的情况:
>>> class A: x = 'a'
...
>>> class B(A): pass
...
>>> class C(A): x = 'c'
...
>>> class D(B, C): pass
...
>>> D.x
'a'
这里,传统型的,分辨率顺序是d - B - 甲 - C - 答:所以仰视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
的C'S倍率)实际上合理工作。
这是老式类应避免的原因之一:多重继承与“钻石般”的模式只是不与他们有效地工作,同时与新的风格确实
。其他提示
Python的方法解析顺序实际上比只是理解菱形图案更加复杂。要的真正的理解,看看 C3线性。我发现它确实有助于使用打印报表时,延长的方法来跟踪订单。例如,您怎么看这种模式的输出会是什么? (注:“X”是假设是两个交叉的边缘,而不是一个节点和^意味着调用super方法(i))
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
你得到A B d C ^ E F G
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()
你得到的结果是正确的。尝试改变基类Base3
来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
读取这个解释获得更多的信息。
您看到这种行为是因为方法解析是深度优先,而不是广度优先。Dervied 的继承看起来像
Base2 -> Base1
/
Derived - Base3
所以 instance.amethod()
- 检查Base2,没有找到方法。
- 看到 Base2 继承自 Base1,并检查 Base1。Base1 有一个
amethod
, ,所以它被调用。
这体现在 Derived.__mro__
. 。简单地迭代一遍 Derived.__mro__
当您找到正在寻找的方法时停止。