2012-01-18 16 views
14

Quando si deriva da un tipo incorporato e da un'altra classe, sembra che il costruttore del tipo incorporato non chiami il costruttore super-classe. Ciò comporta che i metodi __init__ non vengano chiamati per i tipi che vengono dopo il builtin nell'MRO.I tipi built-in Python 3 __init__ non chiama super() .__ init__?

Esempio:

class A: 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     print("A().__init__()") 

class B(list, A): 
    def __init__(self, *args, **kwargs): 
     print("B().__init__() start") 
     super().__init__(*args, **kwargs) 
     print("B().__init__() end") 

if __name__ == '__main__': 
    b = B() 

In questo esempio, A .__ init__ non viene mai chiamato. Quando B è definito come class B(A, list) invece - cambiando l'ordine di ereditarietà - funziona come previsto (cioè viene chiamato A .__ init__).

Questa dipendenza molto sottile dall'ordine di ereditarietà sembra piuttosto non-pitonica, è intesa in questo modo? Significa anche che non devi mai derivare da tipi built-in in gerarchie di classi complesse, perché non puoi sapere dove finisce il built-in nel MRO quando qualcun altro deriva dalle tue classi (horror di manutenzione). Mi sto perdendo qualcosa?

Info extra: Python versione 3.1

+0

Python non ha mai avuto richiamo automatico del superclasse '__init__' metodi – Marcin

+2

Cosa sarebbe chiamare' A .__ init__' con? 'list .__ init__' prende un argomento e genera un errore se ne ottiene ancora. Anche se accettasse altri argomenti arbitrari, sarebbe possibile annullare il primo, quindi ciò che avrebbe visto A sarebbe dipeso dall'ordine di ereditarietà. –

+0

Una domanda simile: http: // StackOverflow.it/questions/3277367/how-does-pythons-super-work-with-multiple-inheritance Con la presa di Guido proveniente dalla risposta a questa domanda: http://python-history.blogspot.com/2010/06/method- resolution-order.html – aganders3

risposta

9

l'uso corretto di super() è piuttosto sottile e richiede una certa attenzione se i metodi che collaborano non hanno tutte la stessa firma. Lo schema usuale per __init__() metodi è il seguente:

class A(object): 
    def __init__(self, param_a, **kwargs): 
     self.param_a = param_a 
     super(A, self).__init__(**kwargs) 

class B(A): 
    def __init__(self, param_b, **kwargs): 
     self.param_b = param_b 
     super(B, self).__init__(**kwargs) 

class C(A): 
    def __init__(self, param_c, **kwargs): 
     self.param_c = param_c 
     super(C, self).__init__(**kwargs) 

class D(B, C): 
    def __init__(self, param_d, **kwargs): 
     self.param_d = param_d 
     super(D, self).__init__(**kwargs) 

d = D(param_a=1, param_b=2, param_c=3, param_d=4) 

Si noti che questo richiede che tutti i metodi di collaborare, e che tutti i metodi hanno bisogno di una firma in qualche modo compatibili per garantire che non importa a questo punto il metodo viene chiamato.

I costruttori di tipi predefiniti non dispongono di firme del costruttore che consentono di partecipare a tale collaborazione. Anche se chiamassero super().__init__() questo sarebbe piuttosto inutile a meno che tutte le firme del costruttore fossero unificate. Quindi alla fine hai ragione - non sono adatti per partecipare a chiamate di costruttori collaborativi.

super() può essere utilizzato solo se entrambi i metodi di collaborazione hanno la stessa firma (ad esempio __setattr__()) o se si utilizza il modello sopra (o un modello simile). L'uso di super() non è l'unico metodo per chiamare i metodi della classe base, però. Se non ci sono "diamanti" nel modello di ereditarietà multipla, è possibile utilizzare chiamate di classe base esplicite, ad esempio B.__init__(self, param_a). Le classi con più classi di base chiamano semplicemente più costruttori. Anche se ci sono diamanti, a volte è possibile utilizzare chiamate esplicite, purché si tenga presente che un __init__() può essere chiamato più volte senza danno.

Se si desidera utilizzare super() per i contructor in ogni caso, non si dovrebbero in effetti utilizzare sottoclassi di tipi built-in (ad eccezione di object) in più gerarchie inheirtance. Qualche ulteriore lettura: