sintesi Exec:
solo Super esegue un metodo basato sulla gerarchia delle classi __mro__
. Se si desidera eseguire più di un metodo con lo stesso nome, è necessario scrivere in modo cooperativo le classi genitore (chiamando super
in modo implicito o esplicito) oppure è necessario eseguire il ciclo su valori __bases__
o __mro__
delle classi figlio.
Il lavoro di super
è delegare parte o tutto una chiamata di metodo ad un metodo esistente nell'albero degli antenati delle classi. La delega può andare ben al di fuori delle classi che controlli. Il nome del metodo delegato deve esistere nel gruppo di classi base.
Il metodo presentato qui di seguito utilizzando __bases__
con try/except
è più vicina ad una risposta completa alla tua domanda di come chiamare il metodo di ogni genitore con lo stesso nome.
super
è utile nella situazione in cui si desidera chiamare uno dei metodi del genitore, ma non si sa quale dei due genitori:
class Parent1(object):
pass
class Parent2(object):
# if Parent 2 had on_start - it would be called instead
# because Parent 2 is left of Parent 3 in definition of Child class
pass
class Parent3(object):
def on_start(self):
print('the ONLY class that has on_start')
class Child(Parent1, Parent2, Parent3):
def on_start(self):
super(Child, self).on_start()
In questo caso, Child
ha tre genitori immediati. Solo uno, Parent3, ha un metodo on_start
. La chiamata a super
risolve che solo Parent3
ha on_start
e che è il metodo che viene chiamato.
Se Child
eredita da più di una classe che ha un metodo on_start
, l'ordine viene risolto da sinistra a destra (come elencato nella definizione della classe) e dal basso verso l'alto (come eredità logica). Viene chiamato solo uno dei metodi e gli altri metodi con lo stesso nome nella gerarchia delle classi sono stati sostituiti.
Quindi, più comunemente:
class GreatGrandParent(object):
pass
class GrandParent(GreatGrandParent):
def on_start(self):
print('the ONLY class that has on_start')
class Parent(GrandParent):
# if Parent had on_start, it would be used instead
pass
class Child(Parent):
def on_start(self):
super(Child, self).on_start()
Se si desidera chiamare più metodi genitori per nome del metodo, è possibile utilizzare __bases__
al posto di super in questo caso e iterare le classi base di Child
senza conoscere le classi in base al nome:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
base.on_start(self)
>>> Child().on_start()
do something
do something else
Se c'è una possibilità una delle cla di base SSES non hai on_start
è possibile utilizzare try/except:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
class Parent3(object):
pass
class Child(Parent1, Parent2, Parent3):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
do something
do something else
"Parent3" does not have an "on_start"
Utilizzando __bases__
agirà simile a super
ma per ogni gerarchia delle classi definite nella definizione Child
. cioè, andrà anche se ogni classe forbearer fino on_start
è soddisfatta volta per ciascun genitore della classe:
class GGP1(object):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
def on_start(self):
print('GP1 do something else')
class Parent1(GP1):
pass
class GGP2(object):
def on_start(self):
print('GGP2 do something')
class GP2(GGP2):
pass
class Parent2(GP2):
pass
class Child(Parent1, Parent2):
def on_start(self):
for base in Child.__bases__:
try:
base.on_start(self)
except AttributeError:
# handle that one of those does not have that method
print('"{}" does not have an "on_start"'.format(base.__name__))
>>> Child().on_start()
GP1 do something else
GGP2 do something
# Note that 'GGP1 do something' is NOT printed since on_start was satisfied by
# a descendant class L to R, bottom to top
Ora immaginate una più complessa struttura di ereditarietà:
Se si desidera il metodo on_start
di ogni forbearer, è possibile utilizzare __mro__
e filtrare le classi che non hanno on_start
come parte del loro __dict__
per quella classe. Altrimenti, si otterrà potenzialmente il metodo on_start
di un pirata. In altre parole, hassattr(c, 'on_start')
è True
per ogni classe che è Child
discendente (eccetto object
in questo caso) poiché Ghengis
ha un attributo on_start
e tutte le classi sono classi discendenti di Ghengis.
** Attenzione - Demo Solo **
class Ghengis(object):
def on_start(self):
print('Khan -- father to all')
class GGP1(Ghengis):
def on_start(self):
print('GGP1 do something')
class GP1(GGP1):
pass
class Parent1(GP1):
pass
class GGP2(Ghengis):
pass
class GP2(GGP2):
pass
class Parent2(GP2):
def on_start(self):
print('Parent2 do something')
class Child(Parent1, Parent2):
def on_start(self):
for c in Child.__mro__[1:]:
if 'on_start' in c.__dict__.keys():
c.on_start(self)
>>> Child().on_start()
GGP1 do something
Parent2 do something
Khan -- father to all
Ma questo ha anche un problema - se Child
viene ulteriormente sottoclasse, allora il bambino del bambino sarà anche ciclo rispetto allo stesso __mro__
catena.
Come affermato da Raymond Hettinger:
super() è nel business del metodo di delegare chiamate a qualche classe in albero degli antenati dell'istanza. Per le chiamate al metodo riordinabili da utilizzare, , le classi devono essere progettate in modo cooperativo. Questo presenta tre facilmente risolvere questioni pratiche:
1) il metodo viene chiamato da super() deve esistere
2) il chiamante e il chiamato devono avere una firma argomento di corrispondenza e
3) ogni occorrenza del metodo deve utilizzare Super()
la soluzione è scrivere classi cooperative che utilizzano uniformemente super
l'elenco antenato o uso creativo del adapter pattern di adattarsi classi non puoi controllare.Questi metodi sono discussi in modo più completo nell'articolo Python’s super() considered super! di Raymond Hettinger.
Prova a leggere [questa domanda/risposte] (http://stackoverflow.com/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance) – nnaelle
possibile duplicato di [Understanding Python super() con i metodi \ _ \ _ init \ _ \ _()] (http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods) – Alexander