2010-05-28 13 views
6

Mi chiedo alcuni dettagli su come ... in opere in Python.Perché Python "per ... in" funziona in modo diverso su un elenco di valori rispetto a un elenco di dizionari?

La mia comprensione è for var in iterable su ogni iterazione crea una variabile, var, legata al valore corrente di iterabile. Pertanto, se si esegue for c in cows; c = cows[whatever], ma la modifica di c all'interno del ciclo non influisce sul valore originale. Tuttavia, sembra funzionare diversamente se stai assegnando un valore a un tasto del dizionario.

cows=[0,1,2,3,4,5] 
for c in cows: 
    c+=2 

#cows is now the same - [0,1,2,3,4,5] 

cows=[{'cow':0},{'cow':1},{'cow':2},{'cow':3},{'cow':4},{'cow':5}] 
for c in cows: 
    c['cow']+=2 

# cows is now [{'cow': 2}, {'cow': 3}, {'cow': 4}, {'cow': 5}, {'cow': 6}, {'cow': 7} 
#so, it's changed the original, unlike the previous example 

vedo uno può usare enumerate per fare la prima opera ad esempio, troppo, ma questa è una storia diversa, immagino.

cows=[0,1,2,3,4,5] 
for i,c in enumerate(cows): 
    cows[i]+=1 

# cows is now [1, 2, 3, 4, 5, 6] 

Perché influenza i valori di elenco originali nel secondo esempio ma non il primo?

[modifica]

Grazie per le risposte. Stavo guardando questo da un punto di vista PHP, dove è possibile utilizzare il simbolo & in foreach per specificare se si sta operando su un riferimento a una copia dell'iterazione. Vedo ora che la vera differenza è un dettaglio di base di come Python lavora con oggetti immutabili.

risposta

4

Non ha nulla a che fare con for ... in .... Cambia il tuo codice da for c in cows: a c = cows[3] (e deduci la riga successiva) in ciascun esempio e vedi l'effetto.

Nel primo esempio, gli elementi di elenco sono oggetti int; sono immutabili. Nel secondo esempio, sono oggetti dict, che sono mutabili.

+0

Ho accettato la tua risposta perché "immutabile" è la parola magica. Ricordo ora che gli int sono oggetti separati in Python, quindi è stato rebinding di quel nome su un altro oggetto nel primo esempio e modificando lo stesso oggetto nel secondo. – JAL

3

Eseguendo un'assegnazione di nome come nel primo ciclo, il nome viene ricondotto. Eseguendo un item item come nel secondo loop si modifica l'oggetto esistente.

4

c è una variabile temporanea, disponibile in entrambi i casi. (Tenete presente che in Python tutte le variabili sono semplicemente riferimenti, legati agli oggetti che rappresentano e possono essere rimbalzati su oggetti diversi. Python è più coerente di certi altri linguaggi in questo senso.)

Nell'esempio del vostro elenco ogni iterazione lega nuovamente c da un numero intero a un altro, lasciando invariato l'elenco originale.

Nell'esempio di dettatura, ogni iterazione accede al dettato a cui c è temporaneamente associato, riconciliando uno dei membri di tale dict su un numero intero diverso.

In entrambi i casi, c viene ignorato alla fine del ciclo, ma poiché nel secondo caso è stata modificata una struttura dati diversa da c, si notano le modifiche al termine del ciclo.

5

In realtà non sta agendo in modo diverso. La modifica di una variabile non equivale alla modifica dell'attributo di una variabile. Vedrete la stessa cosa nel seguente esempio:

a = 1 
b = a 
b = 2 

Qui a è ancora 1. b è stato assegnato un valore diverso e non è più lo stesso di un

a = {"hello": 1} 
b = a 
b["hello"] = 2 

Qui a["hello] restituisce 2 invece di 1. b ha ancora lo stesso valore perché non abbiamo assegnato nulla a e quindi corrisponde a a.Abbiamo cambiato proprietà ["hello"] di b a 2 e dal a e b sono la stessa variabile a["hello"] è anche 2

16

Aiuta a immaginare che cosa accade al riferimento di cui c in ogni iterazione:

[ 0, 1, 2, 3, 4, 5 ] 
^
    | 
    c 

c contiene un riferimento che punta al primo elemento nell'elenco. Quando si esegue c += 2 (vale a dire, c = c + 2, la variabile temporanea c viene riassegnato un nuovo valore. Questo nuovo valore è 2, e c è rimbalzo a questo nuovo valore. L'elenco iniziale è lasciato solo.

[ 0, 1, 2, 3, 4, 5 ] 

    c -> 2 

Ora, nel caso dizionario, ecco cosa c è destinata a durante la prima iterazione:.

[ {'cow':0}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ] 
    ^
    | 
    c 

qui, c punti all'oggetto dizionario {'cow':0} Quando si esegue c['cow'] += 2 (vale a dire, c['cow'] = c['cow'] + 2), l'oggetto dizionario stesso viene modificato, poiché c non viene rimbalzato su un oggetto non correlato. Cioè, c punta ancora a quel primo oggetto dizionario.

[ {'cow':2}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ] 
    ^
    | 
    c 
+0

+1, risposta molto, molto buona! :) – jathanism

+0

+1 per la rappresentazione pittorica – Davy8

1

Nel secondo esempio, avete una lista di oggetti dizionario. c fa riferimento all'oggetto dizionario che viene modificato all'interno dell'ambito del ciclo.

1

Indipendentemente looping, si deve notare che:

some_var = some_object 

lega il nome some_var all'oggetto some_object. L'oggetto precedente (se presente) a cui fa riferimento some_var non è associato.

some_var[some_index] = some_object 

non vincolante/non vincolante some_var; è zucchero sintattico consultato:

some_var.__setitem__(some_index, some_object) 

Ovviamente, some_var punta ancora alla stessa indicizzabile (una sequenza o un mapping) oggetto come prima, che è stata appena modificato.

Problemi correlati