9

在 Python 中,如果您使用相同的方法定义两个类并打算将这两个类作为父类,如:

class A(object):
     def hello(self):
         print "hello from class a" 

和:

class B(object):
     def hello(self):
         print "hello from class b"

当您定义子类并按A和B顺序添加两个父类时:

class C(A, B):
     def __init__(self):
         self.hello()

调用 self.method() 时使用的方法是属于 A 的方法,或者是继承列表中的第一个类:

>>> C()
hello from class a
<__main__.C object at 0x10571e9d0>

虽然这在我所有的测试用例中似乎都是正确的,但我在文档或在线中找不到一个位置,它实际上在任何平台和语言实现中都是安全的。任何人都可以确认可以安全地假设列表中的第一个继承类将始终是在其他类上使用的方法(不管 super().__init__() 调用等),还是将我指向确认这一点的官方文档?

谢谢,

4

2 回答 2

12

是的,如介绍新算法以计算方法分辨率顺序(即C3 线性化)的文档中所述,可以保证。

不使用此算法的mro实现并不真正符合 python 语言(版本 2.3+)。AFAIK 所有当前的实现使用 C3 线性化。


C3 线性化满足局部优先排序和单调性属性。本地优先顺序意味着一个类C(B1, ..., Bn)将按照它们在继承列表中列出的顺序在其mro基类中具有。Bi

单调性可能用一个例子更好地解释:

>>> class A(object): pass
>>> class B(object): pass
>>> class C(object): pass
>>> class D(object): pass
>>> class E(object): pass
>>> class K1(A,B,C): pass
>>> class K2(D,B,E): pass
>>> class K3(D,A):   pass
>>> class Z(K1,K2,K3): pass

python2.2的旧mro(不是单调的),这些是上述类的线性化:

L[A] = A O
L[B] = B O
L[C] = C O
L[D] = D O
L[E] = E O
L[K1]= K1 A B C O
L[K2]= K2 D B E O
L[K3]= K3 D A O
L[Z] = Z K1 K3 A K2 D B C E O
# in current versions of python (2.3+):
# L[Z] = Z K1 K2 K3 D A B C E O

在这里你可以看到,在 的线性化中Z,类A出现在 之前D,而在K3它的线性化中,类出现在之后 D。单调性是线性化的属性,因此在继承时没有这种交换。如果一个类在类的父XY的所有线性化中都在类之前,那么它Y在最终的线性化中也将在类之前。

现在,如果我们考虑一个类C(B1, ..., Bn)。通过局部优先顺序,B1, ..., Bn将在 的线性化中按该顺序找到类C。通过单调性,我们无法在自身Bi之前找到 s 的子类。Bi由此得出 的线性化C,如果存在的话,必须从C和开始B1

请注意,在某些情况下,您无法计算线性化,python 会抱怨,例如:

>>> class A(object):pass
... 
>>> class B(object, A): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cannot create a consistent method resolution
order (MRO) for bases object, A

但是,如果您交换类,则可以线性化层次结构:

>>> class B(A, object): pass
... 
>>> B.mro()
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

如果父类没有共同的基础(object显然其他),那么很明显 的线性化C(B1, ..., Bn)将从B1(除了object)的线性化开始,然后将遵循B2etc 的线性化,并以 的线性化结束Bn

>>> class A(object): pass
... 
>>> class B(object): pass
... 
>>> class A1(A): pass
... 
>>> class A2(A1): pass
... 
>>> class B1(B): pass
... 
>>> class C(object): pass
... 
>>> class C1(C): pass
... 
>>> class C2(C1):pass
... 
>>> class C3(C2): pass
... 
>>> class D(A2, B1, C3): pass
... 
>>> D.mro()
[<class '__main__.D'>, <class '__main__.A2'>, <class '__main__.A1'>, <class '__main__.A'>, <class '__main__.B1'>, <class '__main__.B'>, <class '__main__.C3'>, <class '__main__.C2'>, <class '__main__.C1'>, <class '__main__.C'>, <class 'object'>]

当您在 s 之间有一些公共子类时,事情开始变得很奇怪,Bi在这种情况下,python 会找到您期望的不违反本地优先顺序和单调性的顺序,否则会引发错误。

于 2014-02-09T12:51:01.130 回答
2

以下是详细解释的方法解析顺序:http: //www.python.org/download/releases/2.3/mro/

Python 文档(词汇表)实际上指出了这一点。

根据我的经验,跨实现使用是安全的。__mro__您可以使用或检查分辨率顺序mro()

class A(object):
    def hello(self):
        print 'hello from A'

class B(object):
    def hello(self):
        print 'hello from B'

class C(A, B):
    def __init__(self):
        self.hello()

>>> C()
hello from A
>>> print C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>> print C.mro()
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>]
于 2014-02-09T11:13:11.030 回答