Yes it is guaranteed as described in the document that introduced the new algorithm to compute the method resolution order (which is C3 linearization).
Implementations that don't use this algorithm for their mro
don't really conform to the python language (version 2.3+). AFAIK all current implementation do use C3 linearization.
C3 linearization satisfies the local precedence ordering and monotonicity properties. local precedence ordering means that a class C(B1, ..., Bn)
will have in its mro
the base-classes Bi
in the order they were listed in the inheritance list.
Monotonicity is probably better explained with an example:
>>> 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
The old mro for python2.2 (which is not monotonic), these are the linearizations of the above classes:
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
Here you can see that, in the linearization of Z
, the class A
comes before D
, while in the linearization of K3
it comes after D
. Monotonicity is the property of a linearization such that there are no swaps of this kind when inheriting. If a class X
precedes class Y
in all linearization of the parents of a class, then it will also precedes class Y
in the final linearization.
Now, if we consider a class C(B1, ..., Bn)
. By local precedence order the classes B1, ..., Bn
will be found in that order in the linearization of C
. By monotonicity we cannot find subclasses of Bi
s before the Bi
itself. From this follows that the linearization of C
, if it exists, must start with C
and B1
.
Note that in some cases you can't compute the linearization, and python will complain, for example:
>>> 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
However if you swap the classes it is possible to linearize the hierarchy:
>>> class B(A, object): pass
...
>>> B.mro()
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
If parent classes have no common base (other then object
obviously), then it's clear that the linearization of C(B1, ..., Bn)
will start with the linearization of B1
(except for object
), then will follow the linearization of B2
etc and it will end with the linearization of 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'>]
Things start to get weird when you have some common subclasses between the Bi
s in which case either python finds the order that you'd expect that doesn't violate local precedence order and monotonicity or it will raise an error.