L'ordine di risoluzione dei metodi (MRO) è un meccanismo in Python che determina in quale ordine le classi verranno esplorate nella ricerca di metodi e attributi in caso di ereditarietà multipla.
La linearizzazione C3 (C3 linearization) è l'algoritmo utilizzato per costruire la sequenza di risoluzione dei metodi. Comprendere il MRO è critico nella progettazione di complesse gerarchie di classi per evitare ambiguità e comportamenti inaspettati dei metodi.
È possibile conoscere il MRO per qualsiasi classe utilizzando l'attributo __mro__ o la funzione mro():
class A: def hello(self): print('A') class B(A): def hello(self): print('B') class C(A): def hello(self): print('C') class D(B, C): pass print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) obj = D() obj.hello() # 'B', perché B viene prima di C nel MRO
Non è possibile modificare esplicitamente il MRO, ma è possibile controllare l'ordine di elencazione delle classi di base.
In quale ordine verranno chiamati i metodi se la classe eredita da più genitori? Ad esempio:
class Base: def foo(self): print('Base') class Left(Base): def foo(self): print('Left') super().foo() class Right(Base): def foo(self): print('Right') super().foo() class Child(Left, Right): pass Child().foo()
Molti si aspettano che "foo" venga chiamato prima da Left, poi da Right, poi da Base — ed è così. Tuttavia, se si cambia l'ordine delle classi di base (class Child(Right, Left)), l'ordine cambierà!
Risposta corretta:
L'ordine di chiamata sarà quello definito nel MRO. Nel primo caso — Left, Right, Base. Il MRO è costruito secondo un algoritmo specifico (linearizzazione C3):
[Child, Left, Right, Base, object]
Storia
Ereditarietà multipla della logica, sovrascrittura del metodo
Il team di sviluppatori utilizzava l'ereditarietà multipla per mixin. Si è scoperto che a causa di un ordine errato delle classi di base, la logica condivisa del mixin era stata "sovrascritta" dalla classe figlia, facendo sì che parte della logica aziendale non venisse eseguita, causando così la perdita di dati.
Storia
Viene chiamato il metodo genitore sbagliato
Nel metodo di finalizzazione (__del__) ci si aspettava che venisse chiamato il metodo della classe base. Tuttavia, a causa di una comprensione errata del MRO, è stato chiamato un altro metodo ereditato dal mixin, dove non avveniva la pulizia delle risorse. Di conseguenza — fuga di memoria.
Storia
Utilizzo di super() senza comprendere la catena di chiamate
In un progetto Python 3, il cliente ha riscritto parte del codice con ereditarietà multipla, dimenticando di aggiornare le chiamate tramite super(). Questo ha portato a situazioni di ricorsione infinita, in cui il metodo chiamava continuamente se stesso a causa di un ordine errato di risoluzione dei metodi.