Hinweis: Obwohl JavaScript für diese Website nicht unbedingt erforderlich ist, werden Ihre Interaktionsmöglichkeiten mit den Inhalten eingeschränkt sein. Bitte aktivieren Sie JavaScript für das volle Erlebnis.

Die Method Resolution Order (MRO) von Python 2.3

Von Michele Simionato.

ZusammenfassungDieses Dokument richtet sich an Python-Programmierer, die die in Python 2.3 verwendete C3 Method Resolution Order verstehen möchten. Obwohl es nicht für Neulinge gedacht ist, ist es mit vielen ausgearbeiteten Beispielen recht pädagogisch. Mir sind keine anderen öffentlich zugänglichen Dokumente mit demselben Umfang bekannt, daher sollte es nützlich sein.

Haftungsausschluss

Ich spende dieses Dokument der Python Software Foundation unter der Lizenz von Python 2.3. Wie üblich in diesen Fällen warne ich den Leser, dass das Folgende *korrekt sein sollte*, aber ich gebe keine Garantie. Verwenden Sie es auf eigene Gefahr!

Danksagung

Alle Leute der Python-Mailingliste, die mir ihre Unterstützung geschickt haben. Paul Foley, der auf verschiedene Ungenauigkeiten hingewiesen und mich dazu gebracht hat, den Teil über die lokale Vorrangordnung hinzuzufügen. David Goodger für Hilfe bei der Formatierung in reStructuredText. David Mertz für Hilfe bei der Bearbeitung. Joan G. Stark für die Python-Bilder. Schließlich Guido van Rossum, der dieses Dokument enthusiastisch auf die offizielle Python 2.3-Homepage aufgenommen hat.

                      .-=-.          .--.
          __        .'     '.       /  " )
  _     .'  '.     /   .-.   \     /  .-'\
 ( \   / .-.  \   /   /   \   \   /  /    ^
  \ `-` /   \  `-'   /     \   `-`  /
jgs`-.-`     '.____.'       `.____.'

Der Anfang

Felix qui potuit rerum cognoscere causas -- Vergilius

Alles begann mit einem Post von Samuele Pedroni an die Python-Entwicklungs-Mailingliste [1]. In seinem Post zeigte Samuele, dass die Method Resolution Order von Python 2.2 nicht monoton ist, und schlug vor, sie durch die C3 Method Resolution Order zu ersetzen. Guido stimmte seinen Argumenten zu, und daher verwendet Python 2.3 jetzt C3. Die C3-Methode selbst hat nichts mit Python zu tun, da sie von Leuten entwickelt wurde, die an Dylan arbeiteten, und in einer Arbeit für Lisper beschrieben wird [2]. Die vorliegende Arbeit gibt eine (hoffentlich) lesbare Diskussion des C3-Algorithmus für Pythonistas, die die Gründe für die Änderung verstehen wollen.

Zunächst möchte ich darauf hinweisen, dass das, was ich sagen werde, nur für die *neuen Klassen* gilt, die in Python 2.2 eingeführt wurden: *Klassische Klassen* behalten ihre alte Method Resolution Order, Tiefe zuerst und dann von links nach rechts. Daher gibt es keine Code-Brüche für klassische Klassen; und auch wenn es prinzipiell zu Code-Brüchen für neue Klassen von Python 2.2 kommen könnte, sind die Fälle, in denen die C3-Auflösungsreihenfolge von der Method Resolution Order von Python 2.2 abweicht, in der Praxis so selten, dass keine wirklichen Code-Brüche erwartet werden. Daher

Haben Sie keine Angst!

Darüber hinaus, es sei denn, Sie nutzen die Mehrfachvererbung stark und haben nicht-triviale Hierarchien, müssen Sie den C3-Algorithmus nicht verstehen, und Sie können dieses Papier leicht überspringen. Wenn Sie jedoch wirklich wissen wollen, wie Mehrfachvererbung funktioniert, dann ist dieses Papier für Sie. Die gute Nachricht ist, dass die Dinge nicht so kompliziert sind, wie Sie vielleicht erwarten.

Lassen Sie mich mit einigen grundlegenden Definitionen beginnen.

  1. Gegeben sei eine Klasse C in einer komplizierten Mehrfachvererbungshierarchie. Es ist keine triviale Aufgabe, die Reihenfolge festzulegen, in der Methoden überschrieben werden, d. h. die Reihenfolge der Vorfahren von C festzulegen.
  2. Die Liste der Vorfahren einer Klasse C, einschließlich der Klasse selbst, geordnet vom nächsten Vorfahren zum entferntesten, wird als Klassen-Präzedenzliste oder *Linearisierung* von C bezeichnet.
  3. Die *Method Resolution Order* (MRO) ist die Menge von Regeln, die die Linearizierung konstruieren. In der Python-Literatur wird die Redewendung "die MRO von C" auch als Synonym für die Linearizierung der Klasse C verwendet.
  4. Zum Beispiel ist im Falle einer einzelnen Vererbungshierarchie, wenn C eine Unterklasse von C1 ist und C1 eine Unterklasse von C2 ist, die Linearizierung von C einfach die Liste [C, C1, C2]. Bei Mehrfachvererbungshierarchien ist die Konstruktion der Linearizierung jedoch umständlicher, da es schwieriger ist, eine Linearizierung zu konstruieren, die die *lokale Vorrangordnung* und die *Monotonie* respektiert.
  5. Ich werde die lokale Vorrangordnung später diskutieren, aber ich kann die Definition der Monotonie hier geben. Eine MRO ist monoton, wenn Folgendes gilt: *Wenn C1 in der Linearizierung von C vor C2 kommt, dann kommt C1 in der Linearizierung jeder Unterklasse von C vor C2*. Andernfalls könnte der harmlose Vorgang der Ableitung einer neuen Klasse die Auflösungsreihenfolge von Methoden ändern und potenziell sehr subtile Fehler verursachen. Beispiele, bei denen dies geschieht, werden später gezeigt.
  6. Nicht alle Klassen lassen eine Linearizierung zu. Es gibt Fälle in komplizierten Hierarchien, in denen es nicht möglich ist, eine Klasse abzuleiten, deren Linearizierung alle gewünschten Eigenschaften respektiert.

Hier gebe ich ein Beispiel für diese Situation. Betrachten Sie die Hierarchie

>>> O = object
>>> class X(O): pass
>>> class Y(O): pass
>>> class A(X,Y): pass
>>> class B(Y,X): pass

die mit dem folgenden Vererbungsdiagramm dargestellt werden kann, wobei ich mit O dasObjektKlasse bezeichnet habe, die der Anfang jeder Hierarchie für neue Klassen ist

 -----------
|           |
|    O      |
|  /   \    |
 - X    Y  /
   |  / | /
   | /  |/
   A    B
   \   /
     ?

In diesem Fall ist es nicht möglich, eine neue Klasse C von A und B abzuleiten, da X in A vor Y kommt, aber Y in B vor X kommt, daher wäre die Methodenauflösungsreihenfolge in C mehrdeutig.

Python 2.3 löst in dieser Situation eine Ausnahme aus (TypeError: MRO conflict among bases Y, X) und verbietet dem naiven Programmierer, mehrdeutige Hierarchien zu erstellen. Python 2.2 löst stattdessen keine Ausnahme aus, wählt aber eine *ad hoc* Reihenfolge (in diesem Fall CABXYO).


    _                   .-=-.          .-==-.
   { }      __        .' O o '.       /  -<' )
   { }    .' O'.     / o .-. O \     /  .--v`
   { }   / .-. o\   /O  /   \  o\   /O /
    \ `-` /   \ O`-'o  /     \  O`-`o /
jgs  `-.-`     '.____.'       `.____.'

Die C3 Method Resolution Order

Lassen Sie mich einige einfache Notationen einführen, die für die folgende Diskussion nützlich sein werden. Ich werde die Kurzschreibweise verwenden

C1 C2 ... CN

um die Liste der Klassen [C1, C2, ..., CN] anzugeben.

Der *Kopf* der Liste ist ihr erstes Element

Kopf = C1

während der *Schwanz* der Rest der Liste ist

Schwanz = C2 ... CN.

Ich werde auch die Notation verwenden

C + (C1 C2 ... CN) = C C1 C2 ... CN

um die Summe der Listen [C] + [C1, C2, ..., CN] zu bezeichnen.

Nun kann ich erklären, wie die MRO in Python 2.3 funktioniert.

Betrachten Sie eine Klasse C in einer Mehrfachvererbungshierarchie, wobei C von den Basisklassen B1, B2, ..., BN erbt. Wir wollen die Linearizierung L[C] der Klasse C berechnen. Die Regel lautet:

die Linearizierung von C ist die Summe von C plus das Zusammenführen der Linearizierungen der Eltern und der Liste der Eltern.

In symbolischer Notation

L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)

Insbesondere, wenn C dieObjektKlasse ist, die keine Eltern hat, ist die Linearizierung trivial

L[object] = object.

Im Allgemeinen muss man jedoch die Zusammenführung gemäß der folgenden Vorschrift berechnen

Nehmen Sie den Kopf der ersten Liste, d.h. L[B1][0]; wenn dieser Kopf nicht im Schwanz einer der anderen Listen vorkommt, fügen Sie ihn zur Linearizierung von C hinzu und entfernen Sie ihn aus den Listen in der Zusammenführung, andernfalls betrachten Sie den Kopf der nächsten Liste und nehmen Sie ihn, wenn er ein guter Kopf ist. Wiederholen Sie dann den Vorgang, bis alle Klassen entfernt sind oder es unmöglich ist, gute Köpfe zu finden. In diesem Fall ist es unmöglich, die Zusammenführung zu konstruieren, Python 2.3 wird sich weigern, die Klasse C zu erstellen und wird eine Ausnahme auslösen.

Diese Vorschrift stellt sicher, dass der Zusammenführungsvorgang die Reihenfolge *beibehält*, wenn die Reihenfolge beibehalten werden kann. Wenn die Reihenfolge andererseits nicht beibehalten werden kann (wie im Beispiel der ernsthaften Ordnungsstreitigkeit oben), dann kann die Zusammenführung nicht berechnet werden.

Die Berechnung der Zusammenführung ist trivial, wenn C nur einen Elternteil hat (einfache Vererbung); in diesem Fall

L[C(B)] = C + merge(L[B],B) = C + L[B]

Im Falle der Mehrfachvererbung ist es jedoch umständlicher und ich erwarte nicht, dass Sie die Regel ohne ein paar Beispiele verstehen ;-)


          .-'-.
        /'     `\
      /' _.-.-._ `\
     |  (|)   (|)  |
     |   \__"__/   |
     \    |v.v|    /
      \   | | |   /
       `\ |=^-| /'
         `|=-=|'
          | - |
          |=  |
          |-=-|
    _.-=-=|= -|=-=-._
   (      |___|      )
  ( `-=-=-=-=-=-=-=-` )
  (`-=-=-=-=-=-=-=-=-`)
  (`-=-=-=-=-=-=-=-=-`)
   (`-=-=-=-=-=-=-=-`)
    (`-=-=-=-=-=-=-`)
jgs  `-=-=-=-=-=-=-`

Beispiele

Erstes Beispiel. Betrachten Sie die folgende Hierarchie

>>> 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 diesem Fall kann der Vererbungsdiagramm gezeichnet werden als

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

Die Linearizierungen von O, D, E und F sind trivial

L[O] = O
L[D] = D O
L[E] = E O
L[F] = F O

Die Linearizierung von B kann wie folgt berechnet werden

L[B] = B + merge(DO, EO, DE)

Wir sehen, dass D ein guter Kopf ist, also nehmen wir ihn und müssen nur noch berechnenmerge(O, EO, E). Nun ist O kein guter Kopf, da es im Schwanz der Sequenz EO vorkommt. In diesem Fall besagt die Regel, dass wir zur nächsten Sequenz springen müssen. Dann sehen wir, dass E ein guter Kopf ist; wir nehmen ihn und müssen nur noch berechnenmerge(O, O)was O ergibt. Daher

L[B] =  B D E O

Mit dem gleichen Verfahren findet man

L[C] = C + merge(DO,FO,DF)
     = C + D + merge(O,FO,F)
     = C + D + F + merge(O,O)
     = C D F O

Nun können wir berechnen

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 diesem Beispiel ist die Linearizierung in einer ziemlich schönen Weise gemäß der Vererbungsebene geordnet, in dem Sinne, dass niedrigere Ebenen (d. h. spezialisiertere Klassen) eine höhere Priorität haben (siehe Vererbungsdiagramm). Dies ist jedoch nicht der allgemeine Fall.

Ich überlasse es dem Leser als Übung, die Linearizierung für mein zweites Beispiel zu berechnen

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

Der einzige Unterschied zum vorherigen Beispiel ist die Änderung B(D,E) --> B(E,D); jedoch ändert selbst eine so kleine Modifikation die Reihenfolge der Hierarchie vollständig

                           6
                          ---
Level 3                  | O |
                       /  ---  \
                      /    |    \
                     /     |     \
                    /      |      \
                  ---     ---    ---
Level 2        2 | E | 4 | D |  | F | 5
                  ---     ---    ---
                   \      / \     /
                    \    /   \   /
                     \  /     \ /
                      ---     ---
Level 1            1 | B |   | C | 3
                      ---     ---
                       \       /
                        \     /
                          ---
Level 0                0 | A |
                          ---

Beachten Sie, dass die Klasse E, die sich in der zweiten Ebene der Hierarchie befindet, vor der Klasse C liegt, die sich in der ersten Ebene der Hierarchie befindet, d. h. E ist spezialisierter als C, obwohl sie sich auf einer höheren Ebene befindet.

Ein fauler Programmierer kann die MRO direkt aus Python 2.2 erhalten, da sie in diesem Fall mit der Linearizierung von Python 2.3 übereinstimmt. Es reicht aus, die Methode .mro() der Klasse A aufzurufen

>>> A.mro()
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
<class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>,
<type 'object'>)

Schließlich betrachte ich das im ersten Abschnitt diskutierte Beispiel, das eine ernsthafte Ordnungsstreitigkeit betrifft. In diesem Fall ist es einfach, die Linearizierungen von O, X, Y, A und B zu berechnen

L[O] = 0
L[X] = X O
L[Y] = Y O
L[A] = A X Y O
L[B] = B Y X O

Es ist jedoch unmöglich, die Linearizierung für eine Klasse C zu berechnen, die von A und B erbt

L[C] = C + merge(AXYO, BYXO, AB)
     = C + A + merge(XYO, BYXO, B)
     = C + A + B + merge(XYO, YXO)

An diesem Punkt können wir die Listen XYO und YXO nicht zusammenführen, da X im Schwanz von YXO vorkommt, während Y im Schwanz von XYO vorkommt: Daher gibt es keine guten Köpfe und der C3-Algorithmus stoppt. Python 2.3 löst einen Fehler aus und weigert sich, die Klasse C zu erstellen.


                      __
    (\   .-.   .-.   /_")
     \\_//^\\_//^\\_//
jgs   `"`   `"`   `"`

Schlechte Method Resolution Orders

Eine MRO ist *schlecht*, wenn sie grundlegende Eigenschaften wie die lokale Vorrangordnung und Monotonie bricht. In diesem Abschnitt zeige ich, dass sowohl die MRO für klassische Klassen als auch die MRO für neue Klassen von Python 2.2 schlecht sind.

Es ist einfacher, mit der lokalen Vorrangordnung zu beginnen. Betrachten Sie das folgende Beispiel

>>> F=type('Food',(),{'remember2buy':'spam'})
>>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
>>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error!

mit Vererbungsdiagramm

             O
             |
(buy spam)   F
             | \
             | E   (buy eggs)
             | /
             G

      (buy eggs or spam ?)

Wir sehen, dass die Klasse G von F und E erbt, wobei F *vor* E steht: Daher würden wir erwarten, dass das Attribut *G.remember2buy* von *F.rembermer2buy* geerbt wird und nicht von *E.remember2buy*: Dennoch gibt Python 2.2

>>> G.remember2buy
'eggs'

Dies ist ein Bruch der lokalen Vorrangordnung, da die Reihenfolge in der lokalen Vorrangliste, d. h. der Liste der Eltern von G, in der Python 2.2-Linearisierung von G nicht beibehalten wird

L[G,P22]= G E F object   # F *follows* E

Man könnte argumentieren, dass der Grund, warum F in der Python 2.2-Linearisierung nach E kommt, darin liegt, dass F weniger spezialisiert als E ist, da F die Oberklasse von E ist; dennoch ist der Bruch der lokalen Vorrangordnung ziemlich unintuitiv und fehleranfällig. Dies gilt insbesondere, da er sich von alten Klassen unterscheidet

>>> class F: remember2buy='spam'
>>> class E(F): remember2buy='eggs'
>>> class G(F,E): pass
>>> G.remember2buy
'spam'

In diesem Fall ist die MRO GFEF und die lokale Vorrangordnung wird beibehalten.

Als allgemeine Regel sollten Hierarchien wie die vorherige vermieden werden, da unklar ist, ob F E überschreiben sollte oder umgekehrt. Python 2.3 löst die Mehrdeutigkeit, indem es bei der Erstellung der Klasse G eine Ausnahme auslöst und damit den Programmierer effektiv daran hindert, mehrdeutige Hierarchien zu generieren. Der Grund dafür ist, dass der C3-Algorithmus fehlschlägt, wenn die Zusammenführung

merge(FO,EFO,FE)

nicht berechnet werden kann, weil F im Schwanz von EFO und E im Schwanz von FE vorkommt.

Die wirkliche Lösung ist die Gestaltung einer nicht-mehrdeutigen Hierarchie, d. h. die Ableitung von G von E und F (die spezifischere zuerst) und nicht von F und E; in diesem Fall ist die MRO zweifellos GEF.

           O
           |
           F (spam)
         / |
(eggs)   E |
         \ |
           G
             (eggs, no doubt)

Python 2.3 zwingt den Programmierer, gute (oder zumindest weniger fehleranfällige) Hierarchien zu schreiben.

In ähnlicher Weise möchte ich darauf hinweisen, dass der Python 2.3-Algorithmus schlau genug ist, offensichtliche Fehler zu erkennen, wie die Duplizierung von Klassen in der Elternliste

>>> 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 (sowohl für klassische als auch für neue Klassen) würde in dieser Situation keine Ausnahme auslösen.

Schließlich möchte ich zwei Lektionen hervorheben, die wir aus diesem Beispiel gelernt haben

  1. entgegen dem Namen bestimmt die MRO die Auflösungsreihenfolge von Attributen, nicht nur von Methoden;
  2. das Standardessen für Pythonistas ist Spam! (aber das wussten Sie schon ;-)

                      __
    (\   .-.   .-.   /_")
     \\_//^\\_//^\\_//
jgs   `"`   `"`   `"`

Nachdem wir das Problem der lokalen Vorrangordnung diskutiert haben, betrachten wir nun das Problem der Monotonie. Mein Ziel ist es zu zeigen, dass weder die MRO für klassische Klassen noch die MRO für neue Klassen von Python 2.2 monoton ist.

Um zu beweisen, dass die MRO für klassische Klassen nicht monoton ist, reicht es aus, sich das Diamant-Diagramm anzusehen

   C
  / \
 /   \
A     B
 \   /
  \ /
   D

Man erkennt leicht die Inkonsistenz

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!

Andererseits gibt es keine Probleme mit den MROs von Python 2.2 und 2.3, sie liefern beide

L[D] = D A B C

Guido weist in seinem Aufsatz [3] darauf hin, dass die klassische MRO in der Praxis nicht so schlimm ist, da man für klassische Klassen typischerweise Diamanten vermeiden kann. Aber alle neuen Klassen erben vonObjekt, daher sind Diamanten unvermeidlich und Inkonsistenzen treten in jedem Mehrfachvererbungsdiagramm auf.

Die MRO von Python 2.2 macht es schwierig, aber nicht unmöglich, die Monotonie zu brechen. Das folgende Beispiel, das ursprünglich von Samuele Pedroni bereitgestellt wurde, zeigt, dass die MRO von Python 2.2 nicht monoton ist

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

Hier sind die Linearizierungen gemäß der C3 MRO (der Leser sollte diese Linearizierungen als Übung überprüfen und das Vererbungsdiagramm zeichnen ;-)

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 liefert exakt die gleichen Linearizierungen für A, B, C, D, E, K1, K2 und K3, aber eine andere Linearizierung für Z

L[Z,P22] = Z K1 K3 A K2 D B C E O

Es ist klar, dass diese Linearizierung *falsch* ist, da A vor D kommt, während in der Linearizierung von K3 A *nach* D kommt. Mit anderen Worten, in K3 überschreiben Methoden, die von D abgeleitet sind, Methoden, die von A abgeleitet sind, aber in Z, das immer noch eine Unterklasse von K3 ist, überschreiben Methoden, die von A abgeleitet sind, Methoden, die von D abgeleitet sind! Dies ist ein Verstoß gegen die Monotonie. Darüber hinaus ist die Python 2.2-Linearisierung von Z auch inkonsistent mit der lokalen Vorrangordnung, da die lokale Vorrangliste der Klasse Z [K1, K2, K3] ist (K2 folgt vor K3), während in der Linearizierung von Z K2 *K3* folgt. Diese Probleme erklären, warum die 2.2-Regel zugunsten der C3-Regel verworfen wurde.


                                                         __
   (\   .-.   .-.   .-.   .-.   .-.   .-.   .-.   .-.   /_")
    \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
jgs  `"`   `"`   `"`   `"`   `"`   `"`   `"`   `"`   `"`

Das Ende

Diese Sektion ist für den ungeduldigen Leser, der alle vorherigen Sektionen übersprungen und sofort zum Ende gesprungen ist. Diese Sektion ist auch für den faulen Programmierer, der sein Gehirn nicht strapazieren wollte. Schließlich ist sie für den Programmierer mit etwas Stolz, sonst würde er nicht einen Artikel über die C3 Method Resolution Order in Mehrfachvererbungshierarchien lesen ;-) Diese drei Tugenden, alle zusammen genommen (und *nicht* einzeln), verdienen einen Preis: Der Preis ist ein kurzes Python 2.2-Skript, das es Ihnen ermöglicht, die 2.3 MRO ohne Risiko für Ihr Gehirn zu berechnen. Ändern Sie einfach die letzte Zeile, um mit den verschiedenen Beispielen zu spielen, die ich in diesem Papier besprochen habe.

#<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>

Das ist alles Leute,

viel Spaß!

    __
   ("_\   .-.   .-.   .-.   .-.   .-.   .-.   .-.   .-.   /)
      \\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//^\\_//
jgs    `"`   `"`   `"`   `"`   `"`   `"`   `"`   `"`   `"`

Ressourcen

[1]Der Thread auf python-dev, gestartet von Samuele Pedroni: http://mail.python.org/pipermail/python-dev/2002-October/029035.html
[2]Die Arbeit *A Monotonic Superclass Linearization for Dylan*: https://doi.org/10.1145/236337.236343
[3]Guido van Rossums Aufsatz, *Unifying types and classes in Python 2.2*: https://pythonlang.de/2.2.2/descrintro.html