Risposte:
Con new_list = my_list
, in realtà non hai due elenchi. L'assegnazione copia semplicemente il riferimento all'elenco, non l'elenco effettivo, quindi entrambi new_list
e my_list
fanno riferimento allo stesso elenco dopo l'assegnazione.
Per copiare effettivamente l'elenco, hai varie possibilità:
Puoi usare il list.copy()
metodo incorporato (disponibile da Python 3.3):
new_list = old_list.copy()
Puoi tagliarlo:
new_list = old_list[:]
L' opinione di Alex Martelli (almeno nel 2007 ) su questo è che è una strana sintassi e non ha senso usarla mai . ;) (Secondo lui, il prossimo è più leggibile).
È possibile utilizzare la list()
funzione integrata:
new_list = list(old_list)
Puoi usare generico copy.copy()
:
import copy
new_list = copy.copy(old_list)
Questo è un po 'più lento di list()
perché deve prima scoprire il tipo di dati old_list
.
Se l'elenco contiene oggetti e si desidera copiarli, utilizzare generico copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Ovviamente il metodo più lento e che richiede memoria, ma a volte inevitabile.
Esempio:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return 'Foo({!r})'.format(self.val)
foo = Foo(1)
a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
foo.val = 5
print('original: %r\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
% (a, b, c, d, e, f))
Risultato:
original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
Felix ha già fornito una risposta eccellente, ma ho pensato di fare un confronto veloce dei vari metodi:
copy.deepcopy(old_list)
Copy()
metodo Python puro che copia le classi con deepcopyCopy()
metodo Python puro che non copia le classi (solo dicts / liste / tuple)for item in old_list: new_list.append(item)
[i for i in old_list]
(una comprensione della lista )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
( elenco di sezioni )Quindi il più veloce è il taglio delle liste. Ma essere consapevoli che copy.copy()
, list[:]
e list(list)
, a differenza copy.deepcopy()
e la versione di Python non copiare alcun elenco, dizionari e istanze di classe nella lista, quindi se gli originali cambiano, cambieranno nella lista copiato troppo e viceversa.
(Ecco lo script se qualcuno è interessato o vuole sollevare problemi :)
from copy import deepcopy
class old_class:
def __init__(self):
self.blah = 'blah'
class new_class(object):
def __init__(self):
self.blah = 'blah'
dignore = {str: None, unicode: None, int: None, type(None): None}
def Copy(obj, use_deepcopy=True):
t = type(obj)
if t in (list, tuple):
if t == tuple:
# Convert to a list if a tuple to
# allow assigning to when copying
is_tuple = True
obj = list(obj)
else:
# Otherwise just do a quick slice copy
obj = obj[:]
is_tuple = False
# Copy each item recursively
for x in xrange(len(obj)):
if type(obj[x]) in dignore:
continue
obj[x] = Copy(obj[x], use_deepcopy)
if is_tuple:
# Convert back into a tuple again
obj = tuple(obj)
elif t == dict:
# Use the fast shallow dict copy() method and copy any
# values which aren't immutable (like lists, dicts etc)
obj = obj.copy()
for k in obj:
if type(obj[k]) in dignore:
continue
obj[k] = Copy(obj[k], use_deepcopy)
elif t in dignore:
# Numeric or string/unicode?
# It's immutable, so ignore it!
pass
elif use_deepcopy:
obj = deepcopy(obj)
return obj
if __name__ == '__main__':
import copy
from time import time
num_times = 100000
L = [None, 'blah', 1, 543.4532,
['foo'], ('bar',), {'blah': 'blah'},
old_class(), new_class()]
t = time()
for i in xrange(num_times):
Copy(L)
print 'Custom Copy:', time()-t
t = time()
for i in xrange(num_times):
Copy(L, use_deepcopy=False)
print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t
t = time()
for i in xrange(num_times):
copy.copy(L)
print 'copy.copy:', time()-t
t = time()
for i in xrange(num_times):
copy.deepcopy(L)
print 'copy.deepcopy:', time()-t
t = time()
for i in xrange(num_times):
L[:]
print 'list slicing [:]:', time()-t
t = time()
for i in xrange(num_times):
list(L)
print 'list(L):', time()-t
t = time()
for i in xrange(num_times):
[i for i in L]
print 'list expression(L):', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(L)
print 'list extend:', time()-t
t = time()
for i in xrange(num_times):
a = []
for y in L:
a.append(y)
print 'list append:', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(i for i in L)
print 'generator expression extend:', time()-t
timeit
modulo. inoltre, non è possibile trarre grandi conclusioni da micro benchmark arbitrari come questo.
[*old_list]
dovrebbe essere approssimativamente equivalente a list(old_list)
, ma poiché si tratta della sintassi, non dei percorsi di chiamata delle funzioni generali, risparmierà un po 'in fase di esecuzione (e diversamente da old_list[:]
, che non digita convert, [*old_list]
funziona su qualsiasi iterabile e produce a list
).
timeit
, corse di 50m invece di 100k) vedi stackoverflow.com/a/43220129/3745896
[*old_list]
sembra sovraperformare quasi qualsiasi altro metodo. (vedi la mia risposta collegata nei commenti precedenti)
Mi è stato detto che Python 3.3+ aggiunge illist.copy()
metodo, che dovrebbe essere veloce come lo slicing:
newlist = old_list.copy()
s.copy()
crea una copia superficiale di s
(uguale a s[:]
).
python3.8
, .copy()
è leggermente più veloce rispetto affettare. Vedi sotto la risposta @AaronsHall.
Quali sono le opzioni per clonare o copiare un elenco in Python?
In Python 3, è possibile eseguire una copia superficiale con:
a_copy = a_list.copy()
In Python 2 e 3, puoi ottenere una copia superficiale con un'intera porzione dell'originale:
a_copy = a_list[:]
Esistono due modi semantici per copiare un elenco. Una copia superficiale crea un nuovo elenco degli stessi oggetti, una copia profonda crea un nuovo elenco contenente nuovi oggetti equivalenti.
Una copia superficiale copia solo l'elenco stesso, che è un contenitore di riferimenti agli oggetti nell'elenco. Se gli oggetti contenuti sono mutabili e ne viene modificato uno, la modifica verrà riflessa in entrambi gli elenchi.
Esistono diversi modi per farlo in Python 2 e 3. I modi Python 2 funzioneranno anche in Python 3.
In Python 2, il modo idiomatico di creare una copia superficiale di un elenco è con una porzione completa dell'originale:
a_copy = a_list[:]
Puoi anche ottenere lo stesso risultato passando l'elenco attraverso il costruttore dell'elenco,
a_copy = list(a_list)
ma l'uso del costruttore è meno efficiente:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
In Python 3, gli elenchi ottengono il list.copy
metodo:
a_copy = a_list.copy()
In Python 3.5:
>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125
L'uso di new_list = my_list quindi modifica new_list ogni volta che my_list cambia. Perchè è questo?
my_list
è solo un nome che punta all'elenco effettivo in memoria. Quando dici new_list = my_list
che non stai facendo una copia, stai solo aggiungendo un altro nome che punta all'elenco originale in memoria. Possiamo avere problemi simili quando facciamo copie degli elenchi.
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
L'elenco è solo una matrice di puntatori al contenuto, quindi una copia superficiale copia solo i puntatori e quindi hai due elenchi diversi, ma hanno gli stessi contenuti. Per fare copie dei contenuti, è necessaria una copia profonda.
Per creare una copia profonda di un elenco, in Python 2 o 3, utilizzare deepcopy
nel copy
modulo :
import copy
a_deep_copy = copy.deepcopy(a_list)
Per dimostrare come ciò ci consente di creare nuovi elenchi secondari:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
E così vediamo che l'elenco copiato in profondità è un elenco completamente diverso dall'originale. Potresti attivare la tua funzione, ma non farlo. È probabile che tu crei bug che altrimenti non avresti usando la funzione deepcopy della libreria standard.
eval
Potresti vederlo usato come un modo per deepcopy, ma non farlo:
problematic_deep_copy = eval(repr(a_list))
In 64 bit Python 2.7:
>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206
su 64 bit Python 3.5:
>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
list_copy=[]
for item in list: list_copy.append(copy(item))
ed è molto più veloce.
Ci sono già molte risposte che ti dicono come fare una copia corretta, ma nessuna di esse dice perché la tua "copia" originale non è riuscita.
Python non memorizza i valori in variabili; lega i nomi agli oggetti. Il tuo incarico originale ha preso l'oggetto a cui fa riferimento my_list
e lo lega new_list
anche. Indipendentemente dal nome che usi, c'è ancora un solo elenco, quindi le modifiche apportate quando ti riferisci ad esso come my_list
persisteranno quando si riferiscono ad esso come new_list
. Ciascuna delle altre risposte a questa domanda offre diversi modi di creare un nuovo oggetto a cui legarsi new_list
.
Ogni elemento di un elenco si comporta come un nome, in quanto ogni elemento si lega non esclusivamente a un oggetto. Una copia superficiale crea un nuovo elenco i cui elementi si legano agli stessi oggetti di prima.
new_list = list(my_list) # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]
Per fare in modo che il tuo elenco copi un ulteriore passo, copia ogni oggetto a cui fa riferimento il tuo elenco e associa le copie degli elementi a un nuovo elenco.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Questa non è ancora una copia profonda, perché ogni elemento di un elenco può fare riferimento ad altri oggetti, proprio come l'elenco è associato ai suoi elementi. Per copiare in modo ricorsivo ogni elemento nell'elenco, quindi ogni altro oggetto a cui fa riferimento ciascun elemento e così via: eseguire una copia profonda.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Consultare la documentazione per ulteriori informazioni sui casi d'angolo durante la copia.
Uso thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Partiamo dall'inizio ed esploriamo questa domanda.
Supponiamo quindi di avere due elenchi:
list_1=['01','98']
list_2=[['01','98']]
E dobbiamo copiare entrambi gli elenchi, a partire dal primo elenco:
Quindi, prima proviamo impostando la variabile copy
alla nostra lista originale, list_1
:
copy=list_1
Ora, se stai pensando di copiare la copia dell'elenco_1, allora ti sbagli. La id
funzione può mostrarci se due variabili possono puntare allo stesso oggetto. Proviamo questo:
print(id(copy))
print(id(list_1))
L'output è:
4329485320
4329485320
Entrambe le variabili sono esattamente lo stesso argomento. Sei sorpreso?
Quindi, come sappiamo, Python non memorizza nulla in una variabile, le variabili fanno semplicemente riferimento all'oggetto e l'oggetto memorizza il valore. Qui object è un list
ma abbiamo creato due riferimenti a quello stesso oggetto con due nomi di variabili differenti. Ciò significa che entrambe le variabili puntano allo stesso oggetto, solo con nomi diversi.
Quando lo fai copy=list_1
, sta effettivamente facendo:
Qui nella lista di immagini_1 e copia ci sono due nomi di variabili ma l'oggetto è lo stesso per entrambe le variabili list
Quindi, se provi a modificare l'elenco copiato, modificherà anche l'elenco originale perché l'elenco è solo uno lì, modificherai tale elenco indipendentemente dall'elenco copiato o dall'elenco originale:
copy[0]="modify"
print(copy)
print(list_1)
produzione:
['modify', '98']
['modify', '98']
Quindi ha modificato l'elenco originale:
Passiamo ora a un metodo pythonic per la copia di liste.
copy_1=list_1[:]
Questo metodo risolve il primo problema riscontrato:
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
Così come possiamo vedere il nostro elenco di entrambi con ID diverso e ciò significa che entrambe le variabili puntano a oggetti diversi. Quindi quello che sta succedendo qui è:
Ora proviamo a modificare l'elenco e vediamo se affrontiamo ancora il problema precedente:
copy_1[0]="modify"
print(list_1)
print(copy_1)
L'output è:
['01', '98']
['modify', '98']
Come puoi vedere, ha modificato solo l'elenco copiato. Ciò significa che ha funzionato.
Pensi che abbiamo finito? No. Proviamo a copiare la nostra lista nidificata.
copy_2=list_2[:]
list_2
dovrebbe fare riferimento a un altro oggetto di cui è una copia list_2
. Controlliamo:
print(id((list_2)),id(copy_2))
Otteniamo l'output:
4330403592 4330403528
Ora possiamo supporre che entrambi gli elenchi puntino oggetti diversi, quindi ora proviamo a modificarlo e vediamo che sta dando ciò che vogliamo:
copy_2[0][1]="modify"
print(list_2,copy_2)
Questo ci dà l'output:
[['01', 'modify']] [['01', 'modify']]
Questo può sembrare un po 'confuso, perché lo stesso metodo che abbiamo usato in precedenza ha funzionato. Proviamo a capirlo.
Quando lo fai:
copy_2=list_2[:]
Stai solo copiando l'elenco esterno, non l'elenco interno. Possiamo usare id
ancora una volta la funzione per verificarlo.
print(id(copy_2[0]))
print(id(list_2[0]))
L'output è:
4329485832
4329485832
Quando lo facciamo copy_2=list_2[:]
, questo accade:
Crea la copia dell'elenco ma solo la copia dell'elenco esterno, non la copia dell'elenco nidificato, l'elenco nidificato è lo stesso per entrambe le variabili, quindi se si tenta di modificare l'elenco nidificato, verrà modificato anche l'elenco originale poiché l'oggetto elenco nidificato è lo stesso per entrambi gli elenchi.
Qual'è la soluzione? La soluzione è la deepcopy
funzione.
from copy import deepcopy
deep=deepcopy(list_2)
Controlliamo questo:
print(id((list_2)),id(deep))
4322146056 4322148040
Entrambi gli elenchi esterni hanno ID diversi, proviamo questo sugli elenchi nidificati interni.
print(id(deep[0]))
print(id(list_2[0]))
L'output è:
4322145992
4322145800
Come puoi vedere, entrambi gli ID sono diversi, il che significa che ora possiamo supporre che entrambi gli elenchi nidificati puntino a un oggetto diverso.
Ciò significa che quando fai deep=deepcopy(list_2)
ciò che accade realmente:
Entrambi gli elenchi nidificati puntano oggetti diversi e ora hanno una copia separata dell'elenco nidificato.
Ora proviamo a modificare l'elenco nidificato e vediamo se ha risolto il problema precedente o meno:
deep[0][1]="modify"
print(list_2,deep)
Emette:
[['01', '98']] [['01', 'modify']]
Come puoi vedere, non ha modificato l'elenco nidificato originale, ma ha modificato solo l'elenco copiato.
Ecco i risultati di temporizzazione usando Python 3.6.8. Ricorda che questi tempi sono relativi l'uno all'altro, non assoluti.
Mi sono bloccato a fare solo copie superficiali e ho anche aggiunto alcuni nuovi metodi che non erano possibili in Python2, come list.copy()
(l' equivalente di Python3 slice ) e due forme di decompressione dell'elenco ( *new_list, = list
e new_list = [*list]
):
METHOD TIME TAKEN
b = [*a] 2.75180600000021
b = a * 1 3.50215399999990
b = a[:] 3.78278899999986 # Python2 winner (see above)
b = a.copy() 4.20556500000020 # Python3 "slice equivalent" (see above)
b = []; b.extend(a) 4.68069800000012
b = a[0:len(a)] 6.84498999999959
*b, = a 7.54031799999984
b = list(a) 7.75815899999997
b = [i for i in a] 18.4886440000000
b = copy.copy(a) 18.8254879999999
b = []
for item in a:
b.append(item) 35.4729199999997
Possiamo vedere che il vincitore di Python2 fa ancora bene, ma non supera list.copy()
di molto Python3 , soprattutto considerando la leggibilità superiore di quest'ultimo.
Il cavallo oscuro è il metodo di disimballaggio e reimballaggio ( b = [*a]
), che è ~ 25% più veloce dell'affettatura grezza e più del doppio dell'altro metodo di disimballaggio ( *b, = a
).
b = a * 1
anche sorprendentemente bene.
Si noti che questi metodi non producono risultati equivalenti per qualsiasi input diverso dagli elenchi. Funzionano tutti per oggetti affettabili, alcuni funzionano per qualsiasi iterabile, ma copy.copy()
funzionano solo per oggetti Python più generali.
Ecco il codice di test per le parti interessate ( modello da qui ):
import timeit
COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'
print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a: b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
b=[*a]
- l'unico modo ovvio per farlo;).
Tutti gli altri contributori hanno dato ottime risposte, che funzionano quando si dispone di un elenco a singola dimensione (livellato), tuttavia dei metodi menzionati finora, copy.deepcopy()
funziona solo per clonare / copiare un elenco e non farlo puntare agli list
oggetti nidificati quando si è lavorare con liste nidificate multidimensionali (lista di liste). Mentre Felix Kling si riferisce a questo nella sua risposta, c'è un po 'di più sul problema e probabilmente una soluzione alternativa che utilizza gli incorporati che potrebbero rivelarsi un'alternativa più veloce a deepcopy
.
Mentre new_list = old_list[:]
, copy.copy(old_list)'
e per Py3k old_list.copy()
funzionano per elenchi a livello singolo, tornano a indicare gli list
oggetti nidificati all'interno di old_list
e il new_list
, e le modifiche a uno degli list
oggetti vengono perpetuate nell'altro.
Come sottolineato sia da Aaron Hall che dal PM 2Ring l' utilizzo
eval()
non è solo una cattiva idea, è anche molto più lento dicopy.deepcopy()
.Ciò significa che per gli elenchi multidimensionali, l'unica opzione è
copy.deepcopy()
. Detto questo, in realtà non è un'opzione poiché le prestazioni vanno molto a sud quando si tenta di utilizzarlo su un array multidimensionale di dimensioni moderate. Ho provato atimeit
utilizzare un array 42x42, non inaudito o addirittura così grande per le applicazioni bioinformatiche, e ho rinunciato ad aspettare una risposta e ho appena iniziato a digitare la mia modifica per questo post.Sembrerebbe che l'unica vera opzione sia quella di inizializzare più elenchi e lavorarci su indipendentemente. Se qualcuno ha altri suggerimenti, su come gestire la copia di elenchi multidimensionali, sarebbe apprezzato.
Come altri hanno già affermato, ci sono problemi di prestazioni significativi utilizzando il copy
modulo e copy.deepcopy
per gli elenchi multidimensionali .
repr()
sia sufficiente per ricreare l'oggetto. Inoltre, eval()
è uno strumento di ultima istanza; vedi Eval è davvero pericoloso dal veterano SO Ned Batchelder per i dettagli. Quindi, quando sostenete l'uso, eval()
dovreste davvero dire che può essere pericoloso.
eval()
funzione in Python in generale sia un rischio. Non è tanto se si utilizza o meno la funzione nel codice, ma è un buco di sicurezza in Python in sé e per sé. Il mio esempio non utilizza con una funzione che riceve input da input()
, sys.agrv
o anche un file di testo. È più sulla falsariga di inizializzare un elenco multidimensionale vuoto una volta, e quindi avere solo un modo di copiarlo in un ciclo invece di reinizializzare ad ogni iterazione del ciclo.
new_list = eval(repr(old_list))
, quindi oltre ad essere una cattiva idea, probabilmente è anche troppo lento per funzionare.
Mi sorprende che non sia stato ancora menzionato, quindi per completezza ...
È possibile eseguire il disimballaggio dell'elenco con "l'operatore splat": *
che copia anche gli elementi dell'elenco.
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
L'ovvio aspetto negativo di questo metodo è che è disponibile solo in Python 3.5+.
Per quanto riguarda i tempi, questo sembra funzionare meglio di altri metodi comuni.
x = [random.random() for _ in range(1000)]
%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]
%timeit a = [*x]
#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
old_list
e new_list
sono due elenchi diversi, la modifica di uno non cambierà l'altro (a meno che non si stia mutando direttamente gli elementi stessi (come l'elenco di elenchi), nessuno di questi metodi è copie profonde).
Un approccio molto semplice indipendente dalla versione di Python mancava nelle risposte già fornite che puoi usare la maggior parte del tempo (almeno lo faccio):
new_list = my_list * 1 #Solution 1 when you are not using nested lists
Tuttavia, se my_list contiene altri contenitori (ad esempio elenchi nidificati) è necessario utilizzare deepcopy come altri suggeriti nelle risposte sopra dalla libreria di copie. Per esempio:
import copy
new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists
. Bonus : se non vuoi copiare elementi usa (aka copia superficiale):
new_list = my_list[:]
Comprendiamo la differenza tra la Soluzione n. 1 e la Soluzione n. 2
>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])
Come puoi vedere, la soluzione n. 1 ha funzionato perfettamente quando non stavamo usando gli elenchi nidificati. Controlliamo cosa accadrà quando applicheremo la soluzione n. 1 agli elenchi nidificati.
>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]] #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] #Solution #2 - DeepCopy worked in nested list
Nota che ci sono alcuni casi in cui se hai definito la tua classe personalizzata e vuoi mantenere gli attributi, dovresti usare copy.copy()
o copy.deepcopy()
piuttosto che le alternative, ad esempio in Python 3:
import copy
class MyList(list):
pass
lst = MyList([1,2,3])
lst.name = 'custom list'
d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}
for k,v in d.items():
print('lst: {}'.format(k), end=', ')
try:
name = v.name
except AttributeError:
name = 'NA'
print('name: {}'.format(name))
Uscite:
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
new_list = my_list[:]
new_list = my_list
Cerca di capirlo. Diciamo che my_list è nella memoria heap nella posizione X, ovvero my_list punta alla X. Ora assegnando new_list = my_list
stai lasciando che New_list punta alla X. Questo è noto come copia superficiale.
Ora, se assegni, new_list = my_list[:]
stai semplicemente copiando ogni oggetto di my_list in new_list. Questo è noto come Deep copia.
L'altro modo per farlo è:
new_list = list(old_list)
import copy
new_list = copy.deepcopy(old_list)
Volevo pubblicare qualcosa di leggermente diverso rispetto ad alcune delle altre risposte. Anche se questa probabilmente non è l'opzione più comprensibile o più veloce, fornisce una visione interna di come funziona la copia profonda, oltre ad essere un'altra opzione alternativa per la copia profonda. Non importa se la mia funzione ha dei bug, poiché il punto è quello di mostrare un modo per copiare oggetti come la risposta alla domanda, ma anche di usarlo come punto per spiegare come funziona la deepcopy al suo interno.
Al centro di qualsiasi funzione di copia profonda è il modo di fare una copia superficiale. Come? Semplice. Qualsiasi funzione di copia profonda duplica solo i contenitori di oggetti immutabili. Quando si copia in profondità un elenco nidificato, si duplicano solo gli elenchi esterni, non gli oggetti mutabili all'interno degli elenchi. Stai solo duplicando i contenitori. Lo stesso vale anche per le lezioni. Quando si esegue una copia in profondità di una classe, si esegue la copia in profondità di tutti i suoi attributi mutabili. Così come? Come mai devi solo copiare i contenitori, come liste, dadi, tuple, iter, classi e istanze di classe?
È semplice. Un oggetto mutabile non può davvero essere duplicato. Non può mai essere modificato, quindi è solo un singolo valore. Ciò significa che non devi mai duplicare stringhe, numeri, bool o nessuno di questi. Ma come duplicheresti i contenitori? Semplice. Devi solo inizializzare un nuovo contenitore con tutti i valori. Deepcopy si basa sulla ricorsione. Duplica tutti i contenitori, anche quelli con contenitori all'interno, fino a quando non rimangono più contenitori. Un contenitore è un oggetto immutabile.
Una volta che lo sai, duplicare completamente un oggetto senza riferimenti è abbastanza facile. Ecco una funzione per la copia in profondità dei tipi di dati di base (non funzionerebbe per le classi personalizzate ma potresti sempre aggiungerla)
def deepcopy(x):
immutables = (str, int, bool, float)
mutables = (list, dict, tuple)
if isinstance(x, immutables):
return x
elif isinstance(x, mutables):
if isinstance(x, tuple):
return tuple(deepcopy(list(x)))
elif isinstance(x, list):
return [deepcopy(y) for y in x]
elif isinstance(x, dict):
values = [deepcopy(y) for y in list(x.values())]
keys = list(x.keys())
return dict(zip(keys, values))
La deepcopy integrata di Python si basa su questo esempio. L'unica differenza è che supporta altri tipi e supporta anche le classi utente duplicando gli attributi in una nuova classe duplicata e blocca anche la ricorsione infinita con un riferimento a un oggetto che è già stato visto utilizzando un elenco di memo o un dizionario. E questo è tutto per fare copie profonde. Fondamentalmente, fare una copia profonda è solo fare copie superficiali. Spero che questa risposta aggiunga qualcosa alla domanda.
ESEMPI
Supponi di avere questo elenco: [1, 2, 3] . I numeri immutabili non possono essere duplicati, ma è possibile l'altro livello. Puoi duplicarlo usando una comprensione dell'elenco: [x per x in [1, 2, 3]
Ora, immagina di avere questo elenco: [[1, 2], [3, 4], [5, 6]] . Questa volta, si desidera creare una funzione, che utilizza la ricorsione per copiare in profondità tutti i livelli dell'elenco. Invece della precedente comprensione dell'elenco:
[x for x in _list]
Ne utilizza uno nuovo per gli elenchi:
[deepcopy_list(x) for x in _list]
E deepcopy_list si presenta così:
def deepcopy_list(x):
if isinstance(x, (str, bool, float, int)):
return x
else:
return [deepcopy_list(y) for y in x]
Quindi ora hai una funzione che può deepcopiare qualsiasi elenco di str, bool, floast, in e persino liste su infiniti livelli usando la ricorsione. E il gioco è fatto, deepcopying.
TLDR : Deepcopy utilizza la ricorsione per duplicare oggetti e restituisce semplicemente gli stessi oggetti immutabili di prima, poiché gli oggetti immutabili non possono essere duplicati. Tuttavia, copia in profondità gli strati più interni di oggetti mutabili fino a raggiungere lo strato mutabile più esterno di un oggetto.
Una leggera prospettiva pratica per guardare nella memoria attraverso id e gc.
>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']
>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272)
| |
-----------
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
| | |
-----------------------
>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
| | |
-----------------------
>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word']) # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
| |
-----------
>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
| | |
-----------------------
>>> import gc
>>> gc.get_referrers(a[0])
[['hell', 'word'], ['hell', 'word']] # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None)
Ricordalo in Python quando lo fai:
list1 = ['apples','bananas','pineapples']
list2 = list1
List2 non memorizza l'elenco effettivo, ma un riferimento a list1. Quindi, quando fai qualsiasi cosa in list1, anche list2 cambia. utilizzare il modulo copia (non predefinito, scarica su pip) per creare una copia originale dell'elenco ( copy.copy()
per elenchi semplici, copy.deepcopy()
per quelli nidificati). Questo crea una copia che non cambia con il primo elenco.
L'opzione deepcopy è l'unico metodo che funziona per me:
from copy import deepcopy
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')
porta alla produzione di:
Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Questo perché, la riga new_list = my_list
assegna un nuovo riferimento alla variabile my_list
che new_list
è simile al C
codice indicato di seguito,
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
Utilizzare il modulo copia per creare un nuovo elenco in base a
import copy
new_list = copy.deepcopy(my_list)
newlist = [*mylist]
anche in Python 3.newlist = list(mylist)
è possibile anche se forse è più chiaro.