11.1 Method Resolution Order
L'ordine di risoluzione dei metodi (Method Resolution Order, MRO) determina la sequenza in cui Python cerca metodi e attributi nell'ereditarietà delle classi. È particolarmente importante quando si lavora con l'ereditarietà multipla, quando una classe può ereditare attributi e metodi da più classi genitrici.
In altre parole, esiste un ordine fisso (o meglio, un algoritmo) secondo cui Python attraversa l'albero delle ereditarietà delle classi. Questo algoritmo assicura un corretto ordine di ricerca dei metodi, che può esser descritto come segue:
Algoritmo di linearizzazione C3
L'algoritmo di linearizzazione C3 determina il MRO combinando:
- La classe stessa.
- La lista delle classi genitrici nell'ordine in cui sono elencate.
- MRO delle classi genitrici nello stesso ordine.
Regole dell'algoritmo di linearizzazione C3
-
Mantenere l'ordine locale dei metodi: se la classe
A
è indicata prima della classeB
, tutti i metodi della classeA
devono essere considerati prima dei metodi della classeB
. -
Mantenere l'ordine nei genitori: se la classe
A
è un genitore della classeB
, tutti i metodi della classeA
devono essere considerati prima dei metodi della classeB
. -
Considerare l'ordine di ereditarietà: se la classe
C
è un genitore di due o più classi, l'ordine dei metodi della classeC
deve essere mantenuto nel MRO di tutte queste classi.
Passi dell'algoritmo:
Passo 1. Iniziamo con la classe stessa:
Iniziamo sempre con la classe stessa in cui viene chiamato il metodo.
Passo 2. Aggiungiamo le classi base secondo il loro ordine di elencazione:
Dopo la classe corrente, controlliamo le classi base nell'ordine in cui sono elencate durante l'ereditarietà.
Passo 3. Attraversiamo le classi genitrici:
Cerchiamo campi e metodi lì.
Passo 4. Combiniamo MRO delle classi genitrici:
Se la stessa classe base viene ereditata attraverso più percorsi, viene verificata una sola volta e nell'ordine corretto (tutte le altre volte viene saltata).
Per chi è già familiare con il tema "Algoritmi e strutture dati", questo è un percorso in profondità, non in larghezza.
11.2 Verifica del MRO
In Python è possibile verificare l'ordine di ricerca dei metodi e dei campi della classe,
usando l'attributo __mro__
o la funzione
mro()
.
Esempio:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
def method(self):
print("D")
# Verifica MRO
print(D.__mro__)
L'output sarà:
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
Questo mostra l'ordine in cui Python cercherà metodi e attributi:
D
: Python controlla prima il metodo nella classeD
.-
B
: Poi Python controlla il metodo nella classeB
(primo genitore). -
C
: Se il metodo non viene trovato nella classeB
, Python controlla il metodo nella classeC
(secondo genitore). -
A
: Se il metodo non viene trovato nelle classiB
eC
, Python controlla il metodo nella classeA
. -
object
: Infine, Python controlla il metodo nella classe baseobject
.
11.3 Uso di super()
con MRO
La funzione super()
segue il MRO per chiamare i metodi
delle classi genitrici nel giusto ordine. Consideriamo un esempio di utilizzo
di super()
:
class A:
def method(self):
print("A")
super().method()
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method()
L'output sarà il seguente:
D
B
C
A
Ordine di ricerca (MRO)
1. Chiamata del metodo method
della classe D
:
- Python controlla prima il metodo nella classe
D
e lo trova lì. - Il metodo
D.method()
viene eseguito e stampa"D"
. -
Poi viene chiamato
super().method()
, che segue MRO per chiamare il metodo successivo.
2. Chiamata del metodo method
della classe B
:
- Secondo MRO, la classe successiva dopo
D
èB
. - Il metodo
B.method()
viene eseguito e stampa"B"
. -
Poi viene chiamato
super().method()
, che segue MRO per chiamare il metodo successivo.
3. Chiamata del metodo method
della classe C
:
- La classe successiva nel MRO dopo
B
èC
. - Il metodo
C.method()
viene eseguito e stampa"C"
. -
Poi viene chiamato
super().method()
, che segue MRO per chiamare il metodo successivo.
4. Chiamata del metodo method
della classe A
:
- La classe successiva nel MRO dopo
C
èA
. - Il metodo
A.method()
viene eseguito e stampa"A"
. -
Poi viene chiamato
super().method()
, ma poichéA
non ha metodi genitricimethod
(a parteobject
), la chiamata finisce senza ulteriori azioni.
GO TO FULL VERSION