2012-05-20 15 views
19

Sono al punto di imparare Python in cui ho a che fare con the Mutable Default Argument problem.Perché usare None risolve il problema degli argomenti predefiniti mutabili di Python?

def bad_append(new_item, a_list=[]): 
    a_list.append(new_item) 
    return a_list 

def good_append(new_item, a_list=None): 
    if a_list is None: 
     a_list = [] 
    a_list.append(new_item) 
    return a_list 

Capisco che a_list viene inizializzato solo quando l'istruzione è def primo incontro, ed è per questo le chiamate successive della bad_append utilizzano lo stesso oggetto elenco.

Quello che non capisco è perché good_append funziona in modo diverso. Sembra che a_list sia ancora essere inizializzato solo una volta; pertanto, l'istruzione if sarà vera solo alla prima chiamata della funzione, ovvero a_list verrà reimpostata su [] solo alla prima chiamata, il che significa che accumulerà ancora tutti i valori precedenti a new_item e continuerà a essere bacato.

Perché no? Che concetto mi manca? Come viene pulito a_list ogni volta che viene eseguito good_append?

risposta

11

Il valore predefinito di a_list (o qualsiasi altro valore di default, se è per questo) vengono memorizzati negli interni della funzione una volta che è stato inizializzato e quindi può essere modificato in alcun modo:

>>> def f(x=[]): return x 
... 
>>> f.func_defaults 
([],) 
>>> f.func_defaults[0] is f() 

Quindi il valore in func_defaults è lo stesso che è pure noto funzione (dentro e restituito nel mio esempio per accedere dall'esterno.

IOW, cosa succede quando si chiama f() è un implicito x = f.func_defaults[0]. Se l'oggetto viene modificata successivamente, si manterrà quella modifica

Al contrario, un incarico all'interno di la funzione ottiene sempre un nuovo []. Qualsiasi modifica durerà fino a quando l'ultimo riferimento a quello [] non sarà più disponibile; alla successiva chiamata di funzione, viene creato un nuovo [].

IOW di nuovo, non è vero che [] ottiene lo stesso oggetto su ogni esecuzione, ma è (nel caso di argomento predefinito) eseguito solo una volta e quindi conservato.

+1

Grazie mille; la frase "ciò che accade quando si chiama' f() 'è implicito' x = f.func_defaults [0] '" è stato fondamentale per la mia comprensione. –

+1

... così tanto che sto cambiando idea, di nuovo, e contrassegnando questo come la risposta corretta. –

14

Il problema esiste solo se il valore predefinito è modificabile, che non è None. Ciò che viene memorizzato insieme all'oggetto funzione è il valore predefinito. Quando viene chiamata la funzione, il contesto della funzione viene inizializzato con il valore predefinito.

a_list = [] 

solo assegna un nuovo oggetto al nome a_list nel contesto della chiamata di funzione corrente. Non modifica in alcun modo None.

+0

La mia impressione è che il modello mentale di assegnazione e ambito dell'OP fosse errato. Ho riscritto la risposta per renderlo più chiaro. – phihag

+0

Il mio modello mentale di incarico era davvero errato; anzi, anche ora che capisco meglio il problema, potrebbe ancora esserlo. Quello che non ho capito è che quando si esegue 'a_list = None' nella definizione della funzione, la funzione internamente ha * un altro nome per lo stesso oggetto *, e che il nome visibile del parametro viene riassegnato a quell'oggetto all'inizio di ogni invocazione della funzione. –

4

No, in good_inserta_list non viene inizializzato solo una volta.

Ogni volta che la funzione viene chiamata senza specificare l'argomento a_list, viene utilizzato il valore predefinito e viene utilizzata una nuova istanza di list, il nuovo elenco non sostituisce il valore predefinito.

20

Sembra a_list sarebbe ancora inizializzato solo una volta

"inizializzazione" non è qualcosa che accade a variabili in Python, perché le variabili in Python sono solo nomi. "inizializzazione" avviene solo per oggetti, e viene eseguita tramite il metodo della classe '__init__.

Quando si scrive a = 0, questo è un compito. Questo significa che "a deve fare riferimento all'oggetto che è descritto dall'espressione 0". Non è inizializzazione; a può nominare qualsiasi altro tipo in qualsiasi momento successivo e ciò accade come conseguenza dell'assegnazione di qualcos'altro a a. L'assegnazione è solo un incarico. Il primo non è speciale.

Quando si scrive def good_append(new_item, a_list=None), non è "inizializzato" a_list. Sta configurando un riferimento interno a un oggetto, il risultato della valutazione di None, in modo che quando viene chiamato good_append senza un secondo parametro, tale oggetto viene assegnato automaticamente a a_list.

significa a_list ottiene solo ripristinare [] sulla prima invocazione

No, a_list viene impostata su [] ogni volta che a_list è None per cominciare. Cioè, quando None viene passato esplicitamente o l'argomento è omesso.

Il problema si verifica perché []l'espressione[] è solo valutati volta in questo contesto. Quando viene compilata la funzione, viene valutato [], viene creato un oggetto elenco specifico - che risulta essere vuoto per l'avvio - e tale oggetto viene utilizzato come predefinito.

Come viene pulito a_list ogni volta che si esegue good_append?

Non funziona. Non ha bisogno di essere.

Sapete come si descrive il problema con "argomenti predefiniti mutabili"?

None non è modificabile.

Il problema si verifica quando si modifica l'oggetto che il parametro ha come predefinito.

a_list = [] non modifica l'oggetto a_list precedentemente indicato. Non può; oggetti arbitrari non possono magicamente trasformarsi sul posto in liste vuote. a_list = [] significa "a_list deve smettere di fare riferimento a ciò a cui si riferiva in precedenza e iniziare a fare riferimento a []". L'oggetto precedentemente denominato non è stato modificato.

Quando la funzione è compilata e uno degli argomenti ha un valore predefinito, tale valore - un oggetto - viene inserito nella funzione (che è anch'essa un oggetto!). Quando scrivi un codice che muta un oggetto, l'oggetto muta. Se l'oggetto a cui si fa riferimento è l'oggetto infornato nella funzione, si trasforma ancora in mutazione.

Ma non è possibile modificare None. È immutabile.

È possibile modificare []. È una lista e le liste sono mutabili. L'aggiunta di un elemento a un elenco modifica l'elenco.

+0

Grazie per l'ottima risposta. Mi sto uccidendo cercando di decidere se contrassegnare questa risposta o @ glglgl come corretta. L'altra risposta contiene la singola frase illuminante che mi ha reso in grado di comprendere la tua risposta; la tua risposta nel suo complesso è più completa e comprensibile, ma in qualche modo non ha fatto il clic luce allo stesso modo. Se ci fosse un modo per dare due segni di spunta verdi su una domanda, la tua sarebbe assolutamente l'altra (e potrebbe di nuovo diventare l'unica se continuo a chiacchierare). –

Problemi correlati