2012-02-24 12 views
14

Voglio aggiungere alcuni attributi per il built-in list tipo, quindi ho scritto questo:Python: come posso ereditare dal tipo di lista built-in?

class MyList(list): 
    def __new__(cls, *args, **kwargs): 
     obj = super(MyList, cls).__new__(cls, *args, **kwargs) 
     obj.append('FirstMen') 
     return obj 

    def __init__(self, *args, **kwargs): 
     self.name = 'Westeros' 

    def king(self): 
     print 'IronThrone' 

if __name__ == '__main__': 
    my_list = MyList([1, 2, 3, 4]) 
    print my_list 

ma my_list contiene solo l'elemento 'FirstMen'. Perché il mio __new__ non funziona qui? E come dovrei ereditare da un tipo built-in come list? È lo stesso per i tipi immutabili come str?

+0

Sede [questa domanda] (http://stackoverflow.com/questions/3945940/what-to-consider-before-subclassing-list) per un po ' discussione sul tema della lista di sottoclassi e come spesso non è quello che vuoi. (collections.MutableSequence, se disponibile, potrebbe essere un percorso migliore: leggi [Alex Martelli] (http://stackoverflow.com/a/3488283/487339)). – DSM

+0

possibile duplicato: http://stackoverflow.com/q/4093029/596361 –

risposta

19

Il tipo list di solito esegue l'inizializzazione effettiva dell'elenco all'interno del suo metodo __init__(), in quanto è la convenzione per i tipi mutabili. Devi solo sovrascrivere __new__() quando sottotipo i tipi immutabili. Mentre tu puoi sovrascrivere __new__() quando sottoclassi la lista, non c'è molto punto nel farlo per il tuo caso d'uso. E 'più facile per sovrascrivere solo __init__():

class MyList(list): 
    def __init__(self, *args): 
     list.__init__(self, *args) 
     self.append('FirstMen') 
     self.name = 'Westeros' 

Si noti inoltre che vi consiglio di non utilizzare super() in questo caso. Vuoi chiamare lo list.__init__() qui, e probabilmente non qualcos'altro.

+0

Ah, ho dimenticato di chiamare la '__init__' della superclasse. Ma che ne dici dei tipi immutabili? O c'è una regola generale su quando '__new__' dovrebbe essere sovrascritto? –

+0

@ PJ.Hades: solo sovrascriverlo se necessario.Questo è generalmente il caso se è necessario influenzare la costruzione dei dati immutabili. La sottoclasse di 'tuple' per aggiungere ulteriori metodi e attributi non richiede di sovrascrivere' __new __() '. In caso di dubbio, leggi la documentazione. –

+0

@SvenMarnach, 'self.append ('FirstMen')' - dove compare 'FirstMen' append - a' list' o 'MyList'? –

8

Prima di tutto, stai facendo questo come esercizio per capire __new__? Se no, c'è quasi sicuramente un modo migliore per fare ciò che stai cercando di fare. Potresti spiegare cosa vorresti ottenere qui?

Detto questo, ecco cosa sta succedendo nel tuo esempio:

  1. Invochi MyList([1,2,3,4])
  2. Questa prima invoca MyList.__new__(MyList,[1,2,3,4])
  3. L'implementazione chiama list.__new__(MyList,[1,2,3,4]) Questo restituisce una nuova istanza di MyList, senza elementi. list.__new__ non compila l'elenco. Lo lascia a list.__init__, che non viene mai chiamato.
  4. Il metodo __new__ aggiunge il numero 'FirstMen' all'istanza MyList vuota.
  5. Il metodo __new__ restituisce l'istanza di MyList.
  6. MyList.__init__([1,2,3,4]) viene invocato.
  7. Imposta l'attributo name su 'Westeros'.
  8. Torna.
  9. L'istanza è assegnata a my_list e stampata.

Vedi qui per una spiegazione di __new__: http://docs.python.org/reference/datamodel.html#basic-customization

+0

Sapevo la causa di questo problema. Grazie per la spiegazione :) –

+0

Nota che se 'MyList .__ init __()' * ha * chiama 'list .__ init __()', sostituirà il contenuto della lista con '[1, 2, 3, 4]', così il ''FirstMen'' sarebbe andato. –