2012-01-05 11 views
90

Facciamo un esempiolista python per valore, non con riferimento

a=['help', 'copyright', 'credits', 'license'] 
b=a 
b.append('XYZ') 
b 
['help', 'copyright', 'credits', 'license', 'XYZ'] 
a 
['help', 'copyright', 'credits', 'license', 'XYZ'] 

volevo aggiungere valore nella lista 'B', ma il valore di lista 'A' sono cambiati.
Penso di non avere idea del perché sia ​​così (python passa le liste per riferimento).
La mia domanda è "come posso passarlo per valore in modo che l'aggiunta di" b "non cambi valori in" a "?"

risposta

137

come risposta nel official Python FAQ:

b = a[:] 
+3

Non funziona per me. Qualsiasi modifica apportata a 'b' viene anche vista in' a'. – Mannix

+1

@Mannix Puoi pubblicare il [codice completo] (https://stackoverflow.com/help/mcve) in cui hai mostrato il problema (cioè un'asserzione dovrebbe fallire) in una nuova domanda? Molto probabilmente, non stai modificando la lista stessa, ma i suoi elementi. Crea una [copia profonda] (https://docs.python.org/dev/library/copy.html#copy.deepcopy) se vuoi una nuova lista i cui elementi sono anche copie. – phihag

5

Per creare una copia di un elenco fare questo:

b = a[:] 
13

Inoltre, si può fare:

b = list(a) 

Questo funziona per qualsiasi sequenza, anche quelli che non supportano gli indicizzatori e le porzioni ...

5

Quando si esegue b = a è sufficiente creare un puntatore alla stessa memoria di un, è per questo che quando si accoda a b, un cambiamenti troppo.

È necessario creare copia di un e questo è fatto in questo modo:

b = a[:] 
106

Per copiare un elenco è possibile utilizzare list(a) o a[:]. In entrambi i casi viene creato un nuovo oggetto.
Questi due metodi, però, hanno dei limiti con collezioni di oggetti mutabili come oggetti interni mantengono i loro riferimenti intatta:

>>> a = [[1,2],[3],[4]] 

>>> b = a[:] 
>>> c = list(a) 

>>> c[0].append(9) 

>>> a 
[[1, 2, 9], [3], [4]] 
>>> c 
[[1, 2, 9], [3], [4]] 
>>> b 
[[1, 2, 9], [3], [4]] 
>>> 

Se si desidera una copia completa degli oggetti è necessario copy.deepcopy

>>> from copy import deepcopy 
>>> a = [[1,2],[3],[4]] 

>>> b = a[:] 
>>> c = deepcopy(a) 

>>> c[0].append(9) 

>>> a 
[[1, 2], [3], [4]] 
>>> b 
[[1, 2], [3], [4]] 
>>> c 
[[1, 2, 9], [3], [4]] 
>>> 
+0

qual è la differenza tra una copia normale e una copia profonda? Perché cosa succede sopra succede? Penso di avere una comprensione generale, sembra essere lo stesso problema riscontrato dall'op al secondo livello. Come funziona internamente? – AsheKetchum

2

I trovato che possiamo usare extend() per implementare la funzione di copy()

a=['help', 'copyright', 'credits', 'license'] 
b = [] 
b.extend(a) 
b.append("XYZ") 
18

In termini di prestazioni il mio risposta preferita sarebbe:

b.extend(a) 

controllare come le alternative legate confrontano tra loro in termini di prestazioni:

In [1]: import timeit 

In [2]: timeit.timeit('b.extend(a)', setup='b=[];a=range(0,10)', number=100000000) 
Out[2]: 9.623248100280762 

In [3]: timeit.timeit('b = a[:]', setup='b=[];a=range(0,10)', number=100000000) 
Out[3]: 10.84756088256836 

In [4]: timeit.timeit('b = list(a)', setup='b=[];a=range(0,10)', number=100000000) 
Out[4]: 21.46313500404358 

In [5]: timeit.timeit('b = [elem for elem in a]', setup='b=[];a=range(0,10)', number=100000000) 
Out[5]: 66.99795293807983 

In [6]: timeit.timeit('for elem in a: b.append(elem)', setup='b=[];a=range(0,10)', number=100000000) 
Out[6]: 67.9775960445404 

In [7]: timeit.timeit('b = deepcopy(a)', setup='from copy import deepcopy; b=[];a=range(0,10)', number=100000000) 
Out[7]: 1216.1108016967773 
+1

Grazie per aver apportato le prestazioni alla discussione, questo mi ha aiutato a prendere una decisione sul metodo da utilizzare. – Monkpit

+1

Ho appena trovato la tua risposta, grazie per questa risposta di alta qualità! Quando si discute di Python spesso le prestazioni non vengono considerate e per enormi set di dati fa la differenza. – Artur

+0

Mi piace questa risposta, tuttavia, non si riferisce ai valori della lista. Come menzionato da Jordan Pagni, se la tua lista è multidimensionale, come nelle liste all'interno di elenchi (e altro), l'unica soluzione che funzionerà è quella che impiega più tempo: b = deepcopy (a) –

5

Se si vuole copiare un elenco unidimensionale, utilizzare

b = a[:] 

Tuttavia, se a è un elenco bidimensionale, questo non funzionerà per voi. Cioè, qualsiasi cambiamento in una volontà si rifletterà anche in b. In tal caso, utilizzare

b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))] 
3

Come menzionato da phihag nella sua risposta,

b = a[:] 

funzionerà per il vostro caso poiché affettare un elenco crea un nuovo id di memoria della lista (che significa che non sei più facendo riferimento allo stesso oggetto nella memoria e le modifiche apportate a una non si rifletteranno nell'altra.)

Tuttavia, c'è un piccolo problema. Se la tua lista è multidimensionale, come nelle liste all'interno degli elenchi, semplicemente affettare non risolverà questo problema. Le modifiche apportate alle dimensioni superiori, ovvero le liste all'interno dell'elenco originale, saranno condivise tra le due.

Non preoccuparti, c'è una soluzione. La copia del modulo ha una tecnica di copia nifty che si prende cura di questo problema.

from copy import deepcopy 

b = deepcopy(a) 

copierà un elenco con un nuovo ID di memoria, non importa quanti livelli di liste che contiene!

+0

Ottima risposta, Giordania! Grazie !!! Sai il motivo di questo? – Felipe

1

lo consiglio la seguente soluzione:

b = [] 
b[:] = a 

Questo copierà tutti gli elementi da A a B. La copia sarà copia valore, non copia di riferimento.

Problemi correlati