mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			672 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
		
		
			
		
	
	
			672 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
|   | .. _python_2.3_mro:
 | ||
|  | 
 | ||
|  | The Python 2.3 Method Resolution Order
 | ||
|  | ======================================
 | ||
|  | 
 | ||
|  | .. note::
 | ||
|  | 
 | ||
|  |    This is a historical document, provided as an appendix to the official
 | ||
|  |    documentation.
 | ||
|  |    The Method Resolution Order discussed here was *introduced* in Python 2.3,
 | ||
|  |    but it is still used in later versions -- including Python 3.
 | ||
|  | 
 | ||
|  | By `Michele Simionato <https://www.phyast.pitt.edu/~micheles/>`__.
 | ||
|  | 
 | ||
|  | :Abstract: | ||
|  | 
 | ||
|  |   *This document is intended for Python programmers who want to
 | ||
|  |   understand the C3 Method Resolution Order used in Python 2.3.
 | ||
|  |   Although it is not intended for newbies, it is quite pedagogical with
 | ||
|  |   many worked out examples.  I am not aware of other publicly available
 | ||
|  |   documents with the same scope, therefore it should be useful.*
 | ||
|  | 
 | ||
|  | Disclaimer:
 | ||
|  | 
 | ||
|  |    *I donate this document to the Python Software Foundation, under the
 | ||
|  |    Python 2.3 license.  As usual in these circumstances, I warn the
 | ||
|  |    reader that what follows* should *be correct, but I don't give any
 | ||
|  |    warranty.  Use it at your own risk and peril!*
 | ||
|  | 
 | ||
|  | Acknowledgments:
 | ||
|  | 
 | ||
|  |    *All the people of the Python mailing list who sent me their support.
 | ||
|  |    Paul Foley who pointed out various imprecisions and made me to add the
 | ||
|  |    part on local precedence ordering. David Goodger for help with the
 | ||
|  |    formatting in reStructuredText. David Mertz for help with the editing.
 | ||
|  |    Finally, Guido van Rossum who enthusiastically added this document to
 | ||
|  |    the official Python 2.3 home-page.*
 | ||
|  | 
 | ||
|  | The beginning
 | ||
|  | -------------
 | ||
|  | 
 | ||
|  |                 *Felix qui potuit rerum cognoscere causas* -- Virgilius
 | ||
|  | 
 | ||
|  | Everything started with a post by Samuele Pedroni to the Python
 | ||
|  | development mailing list [#]_.  In his post, Samuele showed that the
 | ||
|  | Python 2.2 method resolution order is not monotonic and he proposed to
 | ||
|  | replace it with the C3 method resolution order.  Guido agreed with his
 | ||
|  | arguments and therefore now Python 2.3 uses C3.  The C3 method itself
 | ||
|  | has nothing to do with Python, since it was invented by people working
 | ||
|  | on Dylan and it is described in a paper intended for lispers [#]_.  The
 | ||
|  | present paper gives a (hopefully) readable discussion of the C3
 | ||
|  | algorithm for Pythonistas who want to understand the reasons for the
 | ||
|  | change.
 | ||
|  | 
 | ||
|  | First of all, let me point out that what I am going to say only applies
 | ||
|  | to the *new style classes* introduced in Python 2.2:  *classic classes*
 | ||
|  | maintain their old method resolution order, depth first and then left to
 | ||
|  | right.  Therefore, there is no breaking of old code for classic classes;
 | ||
|  | and even if in principle there could be breaking of code for Python 2.2
 | ||
|  | new style classes, in practice the cases in which the C3 resolution
 | ||
|  | order differs from the Python 2.2 method resolution order are so rare
 | ||
|  | that no real breaking of code is expected.  Therefore:
 | ||
|  | 
 | ||
|  |    *Don't be scared!*
 | ||
|  | 
 | ||
|  | Moreover, unless you make strong use of multiple inheritance and you
 | ||
|  | have non-trivial hierarchies, you don't need to understand the C3
 | ||
|  | algorithm, and you can easily skip this paper.  On the other hand, if
 | ||
|  | you really want to know how multiple inheritance works, then this paper
 | ||
|  | is for you.  The good news is that things are not as complicated as you
 | ||
|  | might expect.
 | ||
|  | 
 | ||
|  | Let me begin with some basic definitions.
 | ||
|  | 
 | ||
|  | 1) Given a class C in a complicated multiple inheritance hierarchy, it
 | ||
|  |    is a non-trivial task to specify the order in which methods are
 | ||
|  |    overridden, i.e. to specify the order of the ancestors of C.
 | ||
|  | 
 | ||
|  | 2) The list of the ancestors of a class C, including the class itself,
 | ||
|  |    ordered from the nearest ancestor to the furthest, is called the
 | ||
|  |    class precedence list or the *linearization* of C.
 | ||
|  | 
 | ||
|  | 3) The *Method Resolution Order* (MRO) is the set of rules that
 | ||
|  |    construct the linearization.  In the Python literature, the idiom
 | ||
|  |    "the MRO of C" is also used as a synonymous for the linearization of
 | ||
|  |    the class C.
 | ||
|  | 
 | ||
|  | 4) For instance, in the case of single inheritance hierarchy, if C is a
 | ||
|  |    subclass of C1, and C1 is a subclass of C2, then the linearization of
 | ||
|  |    C is simply the list [C, C1 , C2].  However, with multiple
 | ||
|  |    inheritance hierarchies, the construction of the linearization is
 | ||
|  |    more cumbersome, since it is more difficult to construct a
 | ||
|  |    linearization that respects *local precedence ordering* and
 | ||
|  |    *monotonicity*.
 | ||
|  | 
 | ||
|  | 5) I will discuss the local precedence ordering later, but I can give
 | ||
|  |    the definition of monotonicity here.  A MRO is monotonic when the
 | ||
|  |    following is true:  *if C1 precedes C2 in the linearization of C,
 | ||
|  |    then C1 precedes C2 in the linearization of any subclass of C*.
 | ||
|  |    Otherwise, the innocuous operation of deriving a new class could
 | ||
|  |    change the resolution order of methods, potentially introducing very
 | ||
|  |    subtle bugs.  Examples where this happens will be shown later.
 | ||
|  | 
 | ||
|  | 6) Not all classes admit a linearization.  There are cases, in
 | ||
|  |    complicated hierarchies, where it is not possible to derive a class
 | ||
|  |    such that its linearization respects all the desired properties.
 | ||
|  | 
 | ||
|  | Here I give an example of this situation. Consider the hierarchy
 | ||
|  | 
 | ||
|  |   >>> O = object
 | ||
|  |   >>> class X(O): pass
 | ||
|  |   >>> class Y(O): pass
 | ||
|  |   >>> class A(X,Y): pass
 | ||
|  |   >>> class B(Y,X): pass
 | ||
|  | 
 | ||
|  | which can be represented with the following inheritance graph, where I
 | ||
|  | have denoted with O the ``object`` class, which is the beginning of any
 | ||
|  | hierarchy for new style classes:
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |           -----------
 | ||
|  |          |           |
 | ||
|  |          |    O      |
 | ||
|  |          |  /   \    |
 | ||
|  |           - X    Y  /
 | ||
|  |             |  / | /
 | ||
|  |             | /  |/
 | ||
|  |             A    B
 | ||
|  |             \   /
 | ||
|  |               ?
 | ||
|  | 
 | ||
|  | In this case, it is not possible to derive a new class C from A and B,
 | ||
|  | since X precedes Y in A, but Y precedes X in B, therefore the method
 | ||
|  | resolution order would be ambiguous in C.
 | ||
|  | 
 | ||
|  | Python 2.3 raises an exception in this situation (TypeError:  MRO
 | ||
|  | conflict among bases Y, X) forbidding the naive programmer from creating
 | ||
|  | ambiguous hierarchies.  Python 2.2 instead does not raise an exception,
 | ||
|  | but chooses an *ad hoc* ordering (CABXYO in this case).
 | ||
|  | 
 | ||
|  | The C3 Method Resolution Order
 | ||
|  | ------------------------------
 | ||
|  | 
 | ||
|  | Let me introduce a few simple notations which will be useful for the
 | ||
|  | following discussion.  I will use the shortcut notation::
 | ||
|  | 
 | ||
|  |   C1 C2 ... CN
 | ||
|  | 
 | ||
|  | to indicate the list of classes [C1, C2, ... , CN].
 | ||
|  | 
 | ||
|  | The *head* of the list is its first element::
 | ||
|  | 
 | ||
|  |   head = C1
 | ||
|  | 
 | ||
|  | whereas the *tail* is the rest of the list::
 | ||
|  | 
 | ||
|  |   tail = C2 ... CN.
 | ||
|  | 
 | ||
|  | I shall also use the notation::
 | ||
|  | 
 | ||
|  |   C + (C1 C2 ... CN) = C C1 C2 ... CN
 | ||
|  | 
 | ||
|  | to denote the sum of the lists [C] + [C1, C2, ... ,CN].
 | ||
|  | 
 | ||
|  | Now I can explain how the MRO works in Python 2.3.
 | ||
|  | 
 | ||
|  | Consider a class C in a multiple inheritance hierarchy, with C
 | ||
|  | inheriting from the base classes B1, B2, ...  , BN.  We want to
 | ||
|  | compute the linearization L[C] of the class C. The rule is the
 | ||
|  | following:
 | ||
|  | 
 | ||
|  |   *the linearization of C is the sum of C plus the merge of the
 | ||
|  |   linearizations of the parents and the list of the parents.*
 | ||
|  | 
 | ||
|  | In symbolic notation::
 | ||
|  | 
 | ||
|  |    L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
 | ||
|  | 
 | ||
|  | In particular, if C is the ``object`` class, which has no parents, the
 | ||
|  | linearization is trivial::
 | ||
|  | 
 | ||
|  |        L[object] = object.
 | ||
|  | 
 | ||
|  | However, in general one has to compute the merge according to the following
 | ||
|  | prescription:
 | ||
|  | 
 | ||
|  |   *take the head of the first list, i.e L[B1][0]; if this head is not in
 | ||
|  |   the tail of any of the other lists, then add it to the linearization
 | ||
|  |   of C and remove it from the lists in the merge, otherwise look at the
 | ||
|  |   head of the next list and take it, if it is a good head.  Then repeat
 | ||
|  |   the operation until all the class are removed or it is impossible to
 | ||
|  |   find good heads.  In this case, it is impossible to construct the
 | ||
|  |   merge, Python 2.3 will refuse to create the class C and will raise an
 | ||
|  |   exception.*
 | ||
|  | 
 | ||
|  | This prescription ensures that the merge operation *preserves* the
 | ||
|  | ordering, if the ordering can be preserved.  On the other hand, if the
 | ||
|  | order cannot be preserved (as in the example of serious order
 | ||
|  | disagreement discussed above) then the merge cannot be computed.
 | ||
|  | 
 | ||
|  | The computation of the merge is trivial if C has only one parent
 | ||
|  | (single inheritance); in this case::
 | ||
|  | 
 | ||
|  |        L[C(B)] = C + merge(L[B],B) = C + L[B]
 | ||
|  | 
 | ||
|  | However, in the case of multiple inheritance things are more cumbersome
 | ||
|  | and I don't expect you can understand the rule without a couple of
 | ||
|  | examples ;-)
 | ||
|  | 
 | ||
|  | Examples
 | ||
|  | --------
 | ||
|  | 
 | ||
|  | First example. Consider the following hierarchy:
 | ||
|  | 
 | ||
|  |   >>> O = object
 | ||
|  |   >>> class F(O): pass
 | ||
|  |   >>> class E(O): pass
 | ||
|  |   >>> class D(O): pass
 | ||
|  |   >>> class C(D,F): pass
 | ||
|  |   >>> class B(D,E): pass
 | ||
|  |   >>> class A(B,C): pass
 | ||
|  | 
 | ||
|  | In this case the inheritance graph can be drawn as:
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |                             6
 | ||
|  |                            ---
 | ||
|  |   Level 3                 | O |                  (more general)
 | ||
|  |                         /  ---  \
 | ||
|  |                        /    |    \                      |
 | ||
|  |                       /     |     \                     |
 | ||
|  |                      /      |      \                    |
 | ||
|  |                     ---    ---    ---                   |
 | ||
|  |   Level 2        3 | D | 4| E |  | F | 5                |
 | ||
|  |                     ---    ---    ---                   |
 | ||
|  |                      \  \ _ /       |                   |
 | ||
|  |                       \    / \ _    |                   |
 | ||
|  |                        \  /      \  |                   |
 | ||
|  |                         ---      ---                    |
 | ||
|  |   Level 1            1 | B |    | C | 2                 |
 | ||
|  |                         ---      ---                    |
 | ||
|  |                           \      /                      |
 | ||
|  |                            \    /                      \ /
 | ||
|  |                              ---
 | ||
|  |   Level 0                 0 | A |                (more specialized)
 | ||
|  |                              ---
 | ||
|  | 
 | ||
|  | 
 | ||
|  | The linearizations of O,D,E and F are trivial::
 | ||
|  | 
 | ||
|  |   L[O] = O
 | ||
|  |   L[D] = D O
 | ||
|  |   L[E] = E O
 | ||
|  |   L[F] = F O
 | ||
|  | 
 | ||
|  | The linearization of B can be computed as::
 | ||
|  | 
 | ||
|  |   L[B] = B + merge(DO, EO, DE)
 | ||
|  | 
 | ||
|  | We see that D is a good head, therefore we take it and we are reduced to
 | ||
|  | compute ``merge(O,EO,E)``.  Now O is not a good head, since it is in the
 | ||
|  | tail of the sequence EO.  In this case the rule says that we have to
 | ||
|  | skip to the next sequence.  Then we see that E is a good head; we take
 | ||
|  | it and we are reduced to compute ``merge(O,O)`` which gives O. Therefore::
 | ||
|  | 
 | ||
|  |   L[B] =  B D E O
 | ||
|  | 
 | ||
|  | Using the same procedure one finds::
 | ||
|  | 
 | ||
|  |   L[C] = C + merge(DO,FO,DF)
 | ||
|  |        = C + D + merge(O,FO,F)
 | ||
|  |        = C + D + F + merge(O,O)
 | ||
|  |        = C D F O
 | ||
|  | 
 | ||
|  | Now we can compute::
 | ||
|  | 
 | ||
|  |   L[A] = A + merge(BDEO,CDFO,BC)
 | ||
|  |        = A + B + merge(DEO,CDFO,C)
 | ||
|  |        = A + B + C + merge(DEO,DFO)
 | ||
|  |        = A + B + C + D + merge(EO,FO)
 | ||
|  |        = A + B + C + D + E + merge(O,FO)
 | ||
|  |        = A + B + C + D + E + F + merge(O,O)
 | ||
|  |        = A B C D E F O
 | ||
|  | 
 | ||
|  | In this example, the linearization is ordered in a pretty nice way
 | ||
|  | according to the inheritance level, in the sense that lower levels (i.e.
 | ||
|  | more specialized classes) have higher precedence (see the inheritance
 | ||
|  | graph).  However, this is not the general case.
 | ||
|  | 
 | ||
|  | I leave as an exercise for the reader to compute the linearization for
 | ||
|  | my second example:
 | ||
|  | 
 | ||
|  |   >>> O = object
 | ||
|  |   >>> class F(O): pass
 | ||
|  |   >>> class E(O): pass
 | ||
|  |   >>> class D(O): pass
 | ||
|  |   >>> class C(D,F): pass
 | ||
|  |   >>> class B(E,D): pass
 | ||
|  |   >>> class A(B,C): pass
 | ||
|  | 
 | ||
|  | The only difference with the previous example is the change B(D,E) -->
 | ||
|  | B(E,D); however even such a little modification completely changes the
 | ||
|  | ordering of the hierarchy:
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |                              6
 | ||
|  |                             ---
 | ||
|  |   Level 3                  | O |
 | ||
|  |                          /  ---  \
 | ||
|  |                         /    |    \
 | ||
|  |                        /     |     \
 | ||
|  |                       /      |      \
 | ||
|  |                     ---     ---    ---
 | ||
|  |   Level 2        2 | E | 4 | D |  | F | 5
 | ||
|  |                     ---     ---    ---
 | ||
|  |                      \      / \     /
 | ||
|  |                       \    /   \   /
 | ||
|  |                        \  /     \ /
 | ||
|  |                         ---     ---
 | ||
|  |   Level 1            1 | B |   | C | 3
 | ||
|  |                         ---     ---
 | ||
|  |                          \       /
 | ||
|  |                           \     /
 | ||
|  |                             ---
 | ||
|  |   Level 0                0 | A |
 | ||
|  |                             ---
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Notice that the class E, which is in the second level of the hierarchy,
 | ||
|  | precedes the class C, which is in the first level of the hierarchy, i.e.
 | ||
|  | E is more specialized than C, even if it is in a higher level.
 | ||
|  | 
 | ||
|  | A lazy programmer can obtain the MRO directly from Python 2.2, since in
 | ||
|  | this case it coincides with the Python 2.3 linearization.  It is enough
 | ||
|  | to invoke the .mro() method of class A:
 | ||
|  | 
 | ||
|  |   >>> A.mro()  # doctest: +NORMALIZE_WHITESPACE
 | ||
|  |   [<class 'A'>, <class 'B'>, <class 'E'>,
 | ||
|  |   <class 'C'>, <class 'D'>, <class 'F'>,
 | ||
|  |   <class 'object'>]
 | ||
|  | 
 | ||
|  | Finally, let me consider the example discussed in the first section,
 | ||
|  | involving a serious order disagreement.  In this case, it is
 | ||
|  | straightforward to compute the linearizations of O, X, Y, A and B:
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |   L[O] = 0
 | ||
|  |   L[X] = X O
 | ||
|  |   L[Y] = Y O
 | ||
|  |   L[A] = A X Y O
 | ||
|  |   L[B] = B Y X O
 | ||
|  | 
 | ||
|  | However, it is impossible to compute the linearization for a class C
 | ||
|  | that inherits from A and B::
 | ||
|  | 
 | ||
|  |   L[C] = C + merge(AXYO, BYXO, AB)
 | ||
|  |        = C + A + merge(XYO, BYXO, B)
 | ||
|  |        = C + A + B + merge(XYO, YXO)
 | ||
|  | 
 | ||
|  | At this point we cannot merge the lists XYO and YXO, since X is in the
 | ||
|  | tail of YXO whereas Y is in the tail of XYO:  therefore there are no
 | ||
|  | good heads and the C3 algorithm stops.  Python 2.3 raises an error and
 | ||
|  | refuses to create the class C.
 | ||
|  | 
 | ||
|  | Bad Method Resolution Orders
 | ||
|  | ----------------------------
 | ||
|  | 
 | ||
|  | A MRO is *bad* when it breaks such fundamental properties as local
 | ||
|  | precedence ordering and monotonicity.  In this section, I will show
 | ||
|  | that both the MRO for classic classes and the MRO for new style classes
 | ||
|  | in Python 2.2 are bad.
 | ||
|  | 
 | ||
|  | It is easier to start with the local precedence ordering.  Consider the
 | ||
|  | following example:
 | ||
|  | 
 | ||
|  |   >>> F=type('Food',(),{'remember2buy':'spam'})
 | ||
|  |   >>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
 | ||
|  |   >>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!  # doctest: +SKIP
 | ||
|  | 
 | ||
|  | with inheritance diagram
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |                 O
 | ||
|  |                 |
 | ||
|  |    (buy spam)   F
 | ||
|  |                 | \
 | ||
|  |                 | E   (buy eggs)
 | ||
|  |                 | /
 | ||
|  |                 G
 | ||
|  | 
 | ||
|  |          (buy eggs or spam ?)
 | ||
|  | 
 | ||
|  | 
 | ||
|  | We see that class G inherits from F and E, with F *before* E:  therefore
 | ||
|  | we would expect the attribute *G.remember2buy* to be inherited by
 | ||
|  | *F.rembermer2buy* and not by *E.remember2buy*:  nevertheless Python 2.2
 | ||
|  | gives
 | ||
|  | 
 | ||
|  |   >>> G.remember2buy  # doctest: +SKIP
 | ||
|  |   'eggs'
 | ||
|  | 
 | ||
|  | This is a breaking of local precedence ordering since the order in the
 | ||
|  | local precedence list, i.e. the list of the parents of G, is not
 | ||
|  | preserved in the Python 2.2 linearization of G::
 | ||
|  | 
 | ||
|  |   L[G,P22]= G E F object   # F *follows* E
 | ||
|  | 
 | ||
|  | One could argue that the reason why F follows E in the Python 2.2
 | ||
|  | linearization is that F is less specialized than E, since F is the
 | ||
|  | superclass of E; nevertheless the breaking of local precedence ordering
 | ||
|  | is quite non-intuitive and error prone.  This is particularly true since
 | ||
|  | it is a different from old style classes:
 | ||
|  | 
 | ||
|  |   >>> class F: remember2buy='spam'
 | ||
|  |   >>> class E(F): remember2buy='eggs'
 | ||
|  |   >>> class G(F,E): pass  # doctest: +SKIP
 | ||
|  |   >>> G.remember2buy  # doctest: +SKIP
 | ||
|  |   'spam'
 | ||
|  | 
 | ||
|  | In this case the MRO is GFEF and the local precedence ordering is
 | ||
|  | preserved.
 | ||
|  | 
 | ||
|  | As a general rule, hierarchies such as the previous one should be
 | ||
|  | avoided, since it is unclear if F should override E or viceversa.
 | ||
|  | Python 2.3 solves the ambiguity by raising an exception in the creation
 | ||
|  | of class G, effectively stopping the programmer from generating
 | ||
|  | ambiguous hierarchies.  The reason for that is that the C3 algorithm
 | ||
|  | fails when the merge::
 | ||
|  | 
 | ||
|  |    merge(FO,EFO,FE)
 | ||
|  | 
 | ||
|  | cannot be computed, because F is in the tail of EFO and E is in the tail
 | ||
|  | of FE.
 | ||
|  | 
 | ||
|  | The real solution is to design a non-ambiguous hierarchy, i.e. to derive
 | ||
|  | G from E and F (the more specific first) and not from F and E; in this
 | ||
|  | case the MRO is GEF without any doubt.
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  |                 O
 | ||
|  |                 |
 | ||
|  |                 F (spam)
 | ||
|  |               / |
 | ||
|  |      (eggs)   E |
 | ||
|  |               \ |
 | ||
|  |                 G
 | ||
|  |                   (eggs, no doubt)
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Python 2.3 forces the programmer to write good hierarchies (or, at
 | ||
|  | least, less error-prone ones).
 | ||
|  | 
 | ||
|  | On a related note, let me point out that the Python 2.3 algorithm is
 | ||
|  | smart enough to recognize obvious mistakes, as the duplication of
 | ||
|  | classes in the list of parents:
 | ||
|  | 
 | ||
|  |   >>> class A(object): pass
 | ||
|  |   >>> class C(A,A): pass # error
 | ||
|  |   Traceback (most recent call last):
 | ||
|  |     File "<stdin>", line 1, in ?
 | ||
|  |   TypeError: duplicate base class A
 | ||
|  | 
 | ||
|  | Python 2.2 (both for classic classes and new style classes) in this
 | ||
|  | situation, would not raise any exception.
 | ||
|  | 
 | ||
|  | Finally, I would like to point out two lessons we have learned from this
 | ||
|  | example:
 | ||
|  | 
 | ||
|  | 1. despite the name, the MRO determines the resolution order of
 | ||
|  |    attributes, not only of methods;
 | ||
|  | 
 | ||
|  | 2. the default food for Pythonistas is spam !  (but you already knew
 | ||
|  |    that ;-)
 | ||
|  | 
 | ||
|  | Having discussed the issue of local precedence ordering, let me now
 | ||
|  | consider the issue of monotonicity.  My goal is to show that neither the
 | ||
|  | MRO for classic classes nor that for Python 2.2 new style classes is
 | ||
|  | monotonic.
 | ||
|  | 
 | ||
|  | To prove that the MRO for classic classes is non-monotonic is rather
 | ||
|  | trivial, it is enough to look at the diamond diagram:
 | ||
|  | 
 | ||
|  |  .. code-block:: text
 | ||
|  | 
 | ||
|  | 
 | ||
|  |                    C
 | ||
|  |                   / \
 | ||
|  |                  /   \
 | ||
|  |                 A     B
 | ||
|  |                  \   /
 | ||
|  |                   \ /
 | ||
|  |                    D
 | ||
|  | 
 | ||
|  | One easily discerns the inconsistency::
 | ||
|  | 
 | ||
|  |   L[B,P21] = B C        # B precedes C : B's methods win
 | ||
|  |   L[D,P21] = D A C B C  # B follows C  : C's methods win!
 | ||
|  | 
 | ||
|  | On the other hand, there are no problems with the Python 2.2 and 2.3
 | ||
|  | MROs, they give both::
 | ||
|  | 
 | ||
|  |   L[D] = D A B C
 | ||
|  | 
 | ||
|  | Guido points out in his essay [#]_ that the classic MRO is not so bad in
 | ||
|  | practice, since one can typically avoids diamonds for classic classes.
 | ||
|  | But all new style classes inherit from ``object``, therefore diamonds are
 | ||
|  | unavoidable and inconsistencies shows up in every multiple inheritance
 | ||
|  | graph.
 | ||
|  | 
 | ||
|  | The MRO of Python 2.2 makes breaking monotonicity difficult, but not
 | ||
|  | impossible.  The following example, originally provided by Samuele
 | ||
|  | Pedroni, shows that the MRO of Python 2.2 is non-monotonic:
 | ||
|  | 
 | ||
|  |   >>> 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
 | ||
|  | 
 | ||
|  | Here are the linearizations according to the C3 MRO (the reader should
 | ||
|  | verify these linearizations as an exercise and draw the inheritance
 | ||
|  | diagram ;-) ::
 | ||
|  | 
 | ||
|  |   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 K2 K3 D A B C E O
 | ||
|  | 
 | ||
|  | Python 2.2 gives exactly the same linearizations for A, B, C, D, E, K1,
 | ||
|  | K2 and K3, but a different linearization for Z::
 | ||
|  | 
 | ||
|  |   L[Z,P22] = Z K1 K3 A K2 D B C E O
 | ||
|  | 
 | ||
|  | It is clear that this linearization is *wrong*, since A comes before D
 | ||
|  | whereas in the linearization of K3 A comes *after* D. In other words, in
 | ||
|  | K3 methods derived by D override methods derived by A, but in Z, which
 | ||
|  | still is a subclass of K3, methods derived by A override methods derived
 | ||
|  | by D!  This is a violation of monotonicity.  Moreover, the Python 2.2
 | ||
|  | linearization of Z is also inconsistent with local precedence ordering,
 | ||
|  | since the local precedence list of the class Z is [K1, K2, K3] (K2
 | ||
|  | precedes K3), whereas in the linearization of Z K2 *follows* K3.  These
 | ||
|  | problems explain why the 2.2 rule has been dismissed in favor of the C3
 | ||
|  | rule.
 | ||
|  | 
 | ||
|  | The end
 | ||
|  | -------
 | ||
|  | 
 | ||
|  | This section is for the impatient reader, who skipped all the previous
 | ||
|  | sections and jumped immediately to the end.  This section is for the
 | ||
|  | lazy programmer too, who didn't want to exercise her/his brain.
 | ||
|  | Finally, it is for the programmer with some hubris, otherwise s/he would
 | ||
|  | not be reading a paper on the C3 method resolution order in multiple
 | ||
|  | inheritance hierarchies ;-) These three virtues taken all together (and
 | ||
|  | *not* separately) deserve a prize:  the prize is a short Python 2.2
 | ||
|  | script that allows you to compute the 2.3 MRO without risk to your
 | ||
|  | brain.  Simply change the last line to play with the various examples I
 | ||
|  | have discussed in this paper.::
 | ||
|  | 
 | ||
|  |   #<mro.py>
 | ||
|  | 
 | ||
|  |   """C3 algorithm by Samuele Pedroni (with readability enhanced by me)."""
 | ||
|  | 
 | ||
|  |   class __metaclass__(type):
 | ||
|  |       "All classes are metamagically modified to be nicely printed"
 | ||
|  |       __repr__ = lambda cls: cls.__name__
 | ||
|  | 
 | ||
|  |   class ex_2:
 | ||
|  |       "Serious order disagreement" #From Guido
 | ||
|  |       class O: pass
 | ||
|  |       class X(O): pass
 | ||
|  |       class Y(O): pass
 | ||
|  |       class A(X,Y): pass
 | ||
|  |       class B(Y,X): pass
 | ||
|  |       try:
 | ||
|  |           class Z(A,B): pass #creates Z(A,B) in Python 2.2
 | ||
|  |       except TypeError:
 | ||
|  |           pass # Z(A,B) cannot be created in Python 2.3
 | ||
|  | 
 | ||
|  |   class ex_5:
 | ||
|  |       "My first example"
 | ||
|  |       class O: pass
 | ||
|  |       class F(O): pass
 | ||
|  |       class E(O): pass
 | ||
|  |       class D(O): pass
 | ||
|  |       class C(D,F): pass
 | ||
|  |       class B(D,E): pass
 | ||
|  |       class A(B,C): pass
 | ||
|  | 
 | ||
|  |   class ex_6:
 | ||
|  |       "My second example"
 | ||
|  |       class O: pass
 | ||
|  |       class F(O): pass
 | ||
|  |       class E(O): pass
 | ||
|  |       class D(O): pass
 | ||
|  |       class C(D,F): pass
 | ||
|  |       class B(E,D): pass
 | ||
|  |       class A(B,C): pass
 | ||
|  | 
 | ||
|  |   class ex_9:
 | ||
|  |       "Difference between Python 2.2 MRO and C3" #From Samuele
 | ||
|  |       class O: pass
 | ||
|  |       class A(O): pass
 | ||
|  |       class B(O): pass
 | ||
|  |       class C(O): pass
 | ||
|  |       class D(O): pass
 | ||
|  |       class E(O): pass
 | ||
|  |       class K1(A,B,C): pass
 | ||
|  |       class K2(D,B,E): pass
 | ||
|  |       class K3(D,A): pass
 | ||
|  |       class Z(K1,K2,K3): pass
 | ||
|  | 
 | ||
|  |   def merge(seqs):
 | ||
|  |       print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
 | ||
|  |       res = []; i=0
 | ||
|  |       while 1:
 | ||
|  |         nonemptyseqs=[seq for seq in seqs if seq]
 | ||
|  |         if not nonemptyseqs: return res
 | ||
|  |         i+=1; print '\n',i,'round: candidates...',
 | ||
|  |         for seq in nonemptyseqs: # find merge candidates among seq heads
 | ||
|  |             cand = seq[0]; print ' ',cand,
 | ||
|  |             nothead=[s for s in nonemptyseqs if cand in s[1:]]
 | ||
|  |             if nothead: cand=None #reject candidate
 | ||
|  |             else: break
 | ||
|  |         if not cand: raise "Inconsistent hierarchy"
 | ||
|  |         res.append(cand)
 | ||
|  |         for seq in nonemptyseqs: # remove cand
 | ||
|  |             if seq[0] == cand: del seq[0]
 | ||
|  | 
 | ||
|  |   def mro(C):
 | ||
|  |       "Compute the class precedence list (mro) according to C3"
 | ||
|  |       return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])
 | ||
|  | 
 | ||
|  |   def print_mro(C):
 | ||
|  |       print '\nMRO[%s]=%s' % (C,mro(C))
 | ||
|  |       print '\nP22 MRO[%s]=%s' % (C,C.mro())
 | ||
|  | 
 | ||
|  |   print_mro(ex_9.Z)
 | ||
|  | 
 | ||
|  |   #</mro.py>
 | ||
|  | 
 | ||
|  | That's all folks,
 | ||
|  | 
 | ||
|  |                             enjoy !
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Resources
 | ||
|  | ---------
 | ||
|  | 
 | ||
|  | .. [#] The thread on python-dev started by Samuele Pedroni:
 | ||
|  |        https://mail.python.org/pipermail/python-dev/2002-October/029035.html
 | ||
|  | 
 | ||
|  | .. [#] The paper *A Monotonic Superclass Linearization for Dylan*:
 | ||
|  |        https://doi.org/10.1145/236337.236343
 | ||
|  | 
 | ||
|  | .. [#] Guido van Rossum's essay, *Unifying types and classes in Python 2.2*:
 | ||
|  |        https://web.archive.org/web/20140210194412/http://www.python.org/download/releases/2.2.2/descrintro
 |