2016-01-14 5 views
15

Credevo di aver capito operazioni di affettamento Python, ma quando ho provato ad aggiornare un elenco a fette, mi sono confuso:Aggiornamento di un elenco a fette

>>> foo = [1, 2, 3, 4] 
>>> foo[:1] = ['one'] # OK, foo updated 
>>> foo 
['one', 2, 3, 4] 
>>> foo[:][1] = 'two' # why foo not updated? 
>>> foo 
['one', 2, 3, 4] 
>>> foo[:][2:] = ['three', 'four'] # Again, foo not updated 
>>> foo 
['one', 2, 3, 4] 

Perché non viene aggiornato dopo Foo foo[:][1] = 'two'?

Aggiornamento: Forse non ho spiegato chiaramente le mie domande. So che quando si affetta, viene creato un nuovo elenco. Il mio dubbio è perché un'assegnazione delle sezioni aggiorni l'elenco (ad esempio foo[:1] = ['one']), ma se ci sono due livelli di affettamento, non aggiorna l'elenco originale (ad esempio foo[:][2:] = ['three', 'four']).

+6

Hai usato 'numpy' nel passato, forse? Gli array di Numpy usano l'affettatura in modo diverso dagli elenchi Python. –

+0

Per favore leggi https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue –

+0

congratulazioni! hai scoperto [come clonare o copiare un elenco] (http://stackoverflow.com/a/2612815/3904031)! domanda successiva [quanto è andata a finire la copia?] (http://stackoverflow.com/a/26562235/3904031) – uhoh

risposta

17

Questo è perché pitone fa non hanno l -Valori che potrebbero essere assegnati. Invece, alcune espressioni hanno un modulo di assegnazione, che è diverso.

Un foo[something] è uno zucchero sintattico:

foo.__getitem__(something) 

tuttavia il foo[something] = bar è uno zucchero sintattico piuttosto diversa:

foo.__setitem__(something, bar) 

Quando una fetta è solo un caso particolare di something, in modo che foo[x:y] si espande a

foo.__getitem__(slice(x, y, None)) 

e foo[x:y] = bar espande a

foo.__setitem__(slice(x, y, None), bar) 

Ora un __getitem__ con la fetta restituisce una nuova lista che è una copia del campo specificato, quindi la modifica non influenza la matrice originale. E assegnare lavori con la virtù di __setitem__ è un metodo diverso, che può semplicemente fare qualcos'altro.

Tuttavia il trattamento speciale di assegnazione si applica solo all'operazione più esterna. I costituenti sono espressioni normali. Così, quando si scrive

foo[:][1] = 'two' 

esso viene ampliato per

foo.__getitem__(slice(None, None, None)).__setitem__(1, 'two') 

parte foo.__getitem__(slice(None, None, None)) crea una copia e la copia è modificato dalla __setitem__. Ma non l'array originale.

+0

ma perché il taglio parziale, ad esempio, foo [: 1] = ['one'], funziona? – Liang

+0

** Qualsiasi affettatura ** lo fa. È _double_ slicing che non lo fa. –

19

foo[:] è una copia di foo. Hai mutato la copia.

+0

https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue vale la pena aggiungere qui, 'foo [:]' può sembrare lo stesso, ma è un lvalue in 'foo [:] = ...' e un valore in 'foo [:] [2:] = ...' –

+2

@qarma, è non corretto parlare di lvalues ​​e rvalues ​​in Python. Non li ha. In python '[] =' è semplicemente un operatore diverso da '[]'. –

+0

Sì, è corretto, si prega di fornire una spiegazione nella risposta per il resto da vedere. –

3

Usa

foo[1] = 'two' 

e

foo[2:] = ['three', 'four'] 

e funziona.

La risposta perché è nel commento di cui sopra (perché si sta utilizzando una copia)

13

La cosa più importante da notare qui è che foo[:] restituirà una copia di se stesso e quindi l'indicizzazione [1] sarà applicata su l'elenco copiato che è stato restituito

# indexing is applied on copied list 
(foo[:])[1] = 'two' 
    ^
copied list 

È possibile visualizzare questo se si mantiene un riferimento alla lista copiato. Quindi, l'operazione foo[:][1] = 'two' può essere riscritta come:

foo = [1, 2, 3, 4] 

# the following is similar to foo[:][1] = 'two' 

copy_foo = foo[:] 
copy_foo[1] = 'two' 

Ora, copy_foo è stato alterato:

print(copy_foo) 
# [1, 'two', 3, 4] 

Ma, foo rimane la stessa:

print(foo) 
# [1, 2, 3, 4] 

Nel tuo caso , non hai assegnato il nome al risultato intermedio dalla copia dell'elenco foo con foo[:], cioè, non hai tenuto un riferimento ad esso. Dopo che l'assegnazione a 'two' viene eseguita con foo[:][1] = 'two', , l'elenco copiato intermedio cessa di esistere.