2009-12-04 7 views
71

Nel libro Python in a Nutshell (2a edizione) c'è un esempio che utilizza
classi vecchio stile per dimostrare come i metodi vengono risolti in modo classico e risoluzione
come è diverso con il nuovo ordine.Method Resolution Order (MRO) nelle classi di nuovo stile?

Ho provato lo stesso esempio riscrivendo l'esempio in un nuovo stile ma il risultato non è diverso da quello ottenuto con le classi di vecchio stile. La versione python che sto usando per eseguire l'esempio è 2.5.2. Di seguito è l'esempio:

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__ 

La chiamata instance.amethod() stampe Base1, ma come per la mia comprensione della MRO con il nuovo stile di classi dell'uscita avrebbe dovuto essere Base3. Le chiamate Derived.__mro__ stampe:

(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)

io non sono sicuro se la mia comprensione di MRO con nuovi stili di classi non è corretto o che sto facendo un errore stupido che io non sono in grado di rilevare. Per favore aiutami a capire meglio l'MRO.

risposta

144

La differenza cruciale tra l'ordine di risoluzione per le classi precedenti rispetto a quelle di nuovo stile si verifica quando la stessa classe di antenati si verifica più volte nell'approccio "ingenuo", in profondità - ad esempio, considerare un caso di "eredità del diamante":

>>> class A: x = 'a' 
... 
>>> class B(A): pass 
... 
>>> class C(A): x = 'c' 
... 
>>> class D(B, C): pass 
... 
>>> D.x 
'a' 

qui, legacy-style, l'ordine di risoluzione è D - B - a - C - a: così quando alzando lo sguardo Dx, a è la prima base per la risoluzione di risolverlo, nascondendo in tal modo la definizione in C .Mentre:

>>> class A(object): x = 'a' 
... 
>>> class B(A): pass 
... 
>>> class C(A): x = 'c' 
... 
>>> class D(B, C): pass 
... 
>>> D.x 
'c' 
>>> 

qui, di nuovo stile, l'ordine è:

>>> D.__mro__ 
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, 
    <class '__main__.A'>, <type 'object'>) 

con A costretti a venire in ordine di risoluzione solo una volta e dopo tutte le sue sottoclassi, in modo che le sostituzioni (cioè, C di override del membro x) funziona in modo sensato.

È uno dei motivi per cui le classi vecchio stile dovrebbero essere evitate: l'ereditarietà multipla con motivi "a diamante" non funziona in modo sensato con loro, mentre con il nuovo stile.

+1

"[la classe degli antenati] A [è] costretto a venire in ordine di risoluzione solo una volta e dopo tutte le sue sottoclassi, così che le sostituzioni (cioè l'override di C del membro x) funzionano effettivamente in modo ragionevole." - * Epifania! * Grazie a questa frase, posso fare di nuovo MRO nella mia testa. \ o/Grazie mille. – Esteis

5

Il risultato che si ottiene è corretto. Prova a cambiare classe di base di Base3-Base1 e confrontare con la stessa gerarchia per le classi classiche:

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

Ora uscite:

Base3 
Base1 

this explanation Leggi per ulteriori informazioni.

1

Stai vedendo quel comportamento perché la risoluzione del metodo è in profondità, non in ampiezza. l'eredità di Dervied sembra

  Base2 -> Base1 
     /
Derived - Base3 

Così instance.amethod()

  1. Controlli Base2, non trova amethod.
  2. rileva che Base2 ha ereditato da Base1 e controlla Base1. Base1 ha un amethod, quindi viene chiamato.

Ciò si riflette in Derived.__mro__. Basta scorrere su Derived.__mro__ e fermarsi quando trovi il metodo cercato.

+0

Dubito che la ragione per cui ottengo "Base1" come risposta è perché la risoluzione del metodo è approfondita, penso che ci sia molto più di un approccio in profondità. Vedere l'esempio di Denis, se fosse prima la profondità o/p avrebbe dovuto essere "Base1". Fai riferimento anche al primo esempio nel link che hai fornito, inoltre l'MRO mostrato indica che la risoluzione del metodo non è determinata solo dal movimento in profondità in primo ordine. – sateesh

+0

Spiacente, il collegamento al documento su MRO è fornito da Denis. Per favore controlla che, ho scambiato il fatto che mi hai fornito il link a python.org. – sateesh

+4

In genere è in primo luogo la profondità, ma ci sono intelligenze per gestire l'eredità diamantata come ha spiegato Alex. – jamessan

14

L'ordine di risoluzione dei metodi di Python è in realtà più complesso della semplice comprensione del motivo a rombi. Per davvero capirlo, dare un'occhiata a C3 linearization. Ho trovato che aiuta davvero a usare le dichiarazioni di stampa quando si estendono i metodi per tracciare l'ordine. Ad esempio, quale pensi che sarebbe il risultato di questo modello? (Nota: la 'X' è supponiamo di essere due bordi che attraversano, non un nodo e^significa metodi che chiamano super-())

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 

Hai avuto A B D C E F G?

x = A() 
x.m() 

Dopo un sacco di tentativi un errore, mi si avvicinò con un informale un'interpretazione teoria dei grafi C3 linearizzazione come segue: (Qualcuno per favore fatemi sapere se questo è sbagliato.)

Considerate questo esempio:

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() 
+0

Dovresti correggere il tuo secondo codice: hai messo la classe "I" come prima riga e anche usato super così da trovare la super classe "G" ma "I" è di prima classe quindi non sarà mai in grado di trovare la classe "G" perché non c'è la "G" superiore "I". Inserire la classe "I" tra "G" e "F" :) –

+0

Il codice di esempio non è corretto. 'super' ha richiesto argomenti. – danny

+0

All'interno di una definizione di classe super() non sono necessari argomenti. Vedi [https://docs.python.org/3/library/functions.html#super](https://docs.python.org/3/library/functions.html#super) – Ben

Problemi correlati