Domanda

Nel libro Python in a Nutshell (2a edizione) non è un esempio che utilizza
classi vecchio stile per dimostrare come i metodi vengono risolti in ordine di risoluzione classico e
come è diverso con il nuovo ordine.

ho provato lo stesso esempio riscrivendo l'esempio nel nuovo stile, ma il risultato non è diverso da quello che è stato ottenuto con classi di stile vecchi. La versione di Python che sto usando per eseguire l'esempio è 2.5.2 Di seguito è riportato 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__  

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

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

Non sono sicuro se la mia comprensione della MRO con nuovi stili di classi non è corretto o che sto facendo un errore stupido che io non sono in grado di rilevare. Ti prego, aiutami a capire meglio MRO.

È stato utile?

Soluzione

La differenza cruciale tra ordine di risoluzione per l'eredità vs classi di nuovo stile arriva quando la stessa classe antenata si verifica più di una volta nel approccio "naive" in profondità - per esempio, si consideri un caso "diamante eredità":

>>> 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 costretto a venire in ordine risoluzione sola volta e dopo tutte le sue sottoclassi, in modo che le sostituzioni (cioè sostituzione di C dell'elemento x) effettivamente lavoro sensibilmente.

E 'uno dei motivi per cui le classi vecchio stile dovrebbero essere evitati: l'ereditarietà multipla con i modelli "diamond-like" semplicemente non funziona sensibilmente con loro, mentre lo fa con il nuovo stile

.

Altri suggerimenti

ordine di risoluzione metodo di Python è in realtà più complesso di una semplice comprensione del modello di diamante. Per davvero capirlo, un'occhiata a C3 linearizzazione . Ho trovato davvero aiuta ad utilizzare le dichiarazioni di stampa quando si estende metodi per tenere traccia dell'ordine. Ad esempio, cosa ne pensi l'uscita di questo modello sarebbe? (Nota: la 'X' è supponiamo di essere due bordi che attraversano, non un nodo e ^ indica i 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 grafico informale un'interpretazione teoria della linearizzazione C3 come segue: (. Qualcuno per favore fatemi sapere se questo è sbagliato)

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

Il risultato che si ottiene è corretto. Prova a cambiare classe di base di Base3 per 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

questa spiegazione per ulteriori informazioni.

Si sta vedendo che il comportamento perché la risoluzione metodo è in profondità, non in ampiezza. l'eredità di Dervied assomiglia

         Base2 -> Base1
        /
Derived - Base3

Quindi instance.amethod()

  1. Controlla Base2, non trova amethod.
  2. vede che Base2 ha ereditato da Base1, e controlla Base1. Base1 ha un amethod, in modo che viene chiamato.

Ciò si riflette in Derived.__mro__. È sufficiente iterare su Derived.__mro__ e fermarsi quando si trova il metodo di essere cercato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top