MRO - Method Resolution Order
Linearizzazione del DAG delle superclassi
class D: pass
class E: pass
class A(D): pass
class B(E): pass
class C(A,D,B): passflowchart LR
A --> D
B --> E
C --> A
C --> D
C --> B
E --> object
D --> object
Usando la funzione mro() su una di queste sottoclassi, come ad esempio C, viene restituita un array come il seguente:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class 'object'>]Quindi per la ricerca degli attributi, il grafo è stato linearizzato come segue: [C, A, D, B, E, object].
ATTENZIONE
Non tutti i DAG possono essere linearizzati!
class B: pass
class A(object, B): passGenera errore, in quanto entrambe le classi, A e B puntano a object, e si verrebbe a creare un grafo del genere:
flowchart LR
B --> object
A --> object
A --> B
Linearizzazione
In generale viene usato un metodo depth-first/left-to-right del DAG per risolverla.
MonotonicitĂ
Se precede nella linearizzazione di C, allora questo avverrĂ per la linearizzazione di tutte le sottoclassi di C.
Precedenza locale
L’ordine delle classi base della definizione di una classe, è preservato nella linearizzazione delle sottoclassi.
class D: pass
class C(D): pass
class B(C): pass
class G: pass
class F(G): pass
class E(F): pass
class J: pass
class I(J): pass
class H(I): pass
class A(B,E,H): passLe classi qui sopra si possono visualizzare meglio con un grafico:
graph BT
A --> B
A --> E
A --> H
B --> C
C --> D
E --> F
F --> G
H --> I
I --> J
D --> object
G --> object
J --> object
Algoritmo di linearizzazione
Terminologia:
- grafo costituito da tutti e soli i vertici raggiungibili da S
- linearizzazione di , rappresentata come una lista
- linearizzazione di un generico , con testa e coda della linearizzazione
- Se la linearizzazione è composta da
object. passo base della ricorsione.Sè la classe sulla quale si linearizza, e sono le classi base diS. - Si suppone che ricorsivamente siano già stati linearizzati i grafi delle classi base e le liste si indicano con .
- Oltre a quelle liste si considera sempre un’altra lista delle classi base della classe che stiamo considerando,
- Una volta che si hanno tutte le liste, si fa il merge di queste liste.
- Si inizializza la lista
- Le liste vengono analizzate da sinistra a destra un elemento alla volta. Per ogni elemento si possono eseguire due azioni:
- viene inserito in fondo alla lista .
- Se non compare nella coda di nessuna lista con , , viene rimosso da tutte le liste, con indice maggiore della lista che stiamo valutando (), che lo contengono.
- Si ricomincia il processo con la testa della prima lista non vuota tra quelle rimaste.
- Se sono tutte vuote l’algoritmo termina con successo.
- viene temporaneamente scartato.
- Se compare nella coda di una lista con , viene scartato perchè viola la regola di monotonicità , dal momento che dovrebbe essere dopo . Per la stessa ragione non si possono inserire tutti i restanti elementi di .
- Si cerca quindi la testa della prima lista non vuota di indice che non sia un elemento scartato. Se si arriva in fondo alle liste senza trovare un elemento inseribile la linearizzazione fallisce.
- viene inserito in fondo alla lista .
Esempio
class F: pass
class E: pass
class D: pass
class C(D,F): pass
class B(D,E): pass
class A(B,C): pass-
- Si esegue il merge
Bnon compare in nessun’altra lista, quindi , eDcompare nella coda di , quindi bisogna passare a valutare la testa diCnon compare in nessun’altra lista, quindi , e- Ripartendo dalla prima lista, si ha
D, che è in testa a tutte le liste, e quindi , e Ecompare solo in , quindi eOcompare anche in , quindi si inserisceFe si ha , e- Si finisce con e tutte le altre listevuote.
title: Attenzione
La lista $B_i$ serve a evitare violazioni della regola di precedenza locale.
`class C: pass`
`class B(C): pass`
`class A(C, B): pass`
Avendo liste $CO$, $BCO$ e $CB$, si ha che il grafo non è linearizzabile.