python copy - Come clonare o copiare un elenco?




list c# (16)

Quali sono le opzioni per clonare o copiare un elenco in Python?

Usando new_list = my_list modifica quindi new_list ogni volta che my_list cambia.
Perchè è questo?


Answers

L'idioma di Python per farlo è newList = oldList[:]


Felix ha già fornito una risposta eccellente, ma ho pensato di fare un confronto veloce dei vari metodi:

  1. 10,59 sec (105,9us / itn) - copy.deepcopy(old_list)
  2. Metodo 10.16 sec (101.6us / itn) - puro python Copy() che copia le classi con deepcopy
  3. 1.488 secondi (14.88us / itn) - metodo puro python Copy() non copia le classi (solo dicts / liste / tuple)
  4. 0,325 sec (3.25us / itn) - for item in old_list: new_list.append(item)
  5. 0.217 sec (2.17us / itn) - [i for i in old_list] (una lista di comprensione )
  6. 0,186 sec (1.86us / itn) - copy.copy(old_list)
  7. 0.075 sec (0.75us / itn) - list(old_list)
  8. 0,053 sec (0,53us / itn) - new_list = []; new_list.extend(old_list) new_list = []; new_list.extend(old_list)
  9. 0.039 sec (0.39us / itn) - old_list[:] ( elenco slicing )

Quindi il più veloce è l'affettamento delle liste. Ma copy.copy() che copy.copy() , list[:] e list(list) , a differenza di copy.deepcopy() e la versione python non copiano nessun elenco, dizionari e istanze di classe nell'elenco, quindi se gli originali cambiano , cambieranno anche nella lista copiata e viceversa.

(Ecco lo script se qualcuno è interessato o vuole sollevare eventuali 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

EDIT : aggiunto classi e datt nuove di stile vecchio stile ai benchmark, e ha reso la versione python molto più veloce e ha aggiunto altri metodi tra cui list expression ed extend() .


Ci sono già molte risposte che ti dicono come fare una copia corretta, ma nessuno di loro dice perché la tua "copia" originale è fallita.

Python non memorizza valori nelle variabili; lega i nomi agli oggetti. Il tuo compito originale ha preso l'oggetto a cui si riferisce my_list e lo ha anche associato a new_list . Indipendentemente dal nome che utilizzi, esiste ancora una sola lista, quindi le modifiche apportate quando ci si riferisce ad essa come my_list persistono quando ci si riferisce ad essa come new_list . Ciascuna delle altre risposte a questa domanda ti offre diversi modi per creare un nuovo oggetto da associare a new_list .

Ogni elemento di una lista si comporta come un nome, in quanto ogni elemento si lega non esclusivamente ad un oggetto. Una copia superficiale crea una nuova lista 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 la tua lista copi un ulteriore passo, copia ogni oggetto a cui si riferisce la tua lista e associa quelle copie di elementi a una nuova lista.

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 una lista può riferirsi ad altri oggetti, proprio come la lista è legata ai suoi elementi. Per copiare ricorsivamente ogni elemento dell'elenco e 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)

Vedere la documentazione per ulteriori informazioni sui casi d'angolo nella copia.


Un approccio molto semplice, indipendente dalla versione python, mancava nelle risposte già date che puoi usare la maggior parte del tempo (almeno io):

new_list = my_list * 1       #Solution 1 when you are not using nested lists

Tuttavia, se my_list contiene altri contenitori (ad esempio, elenchi annidati), è 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 si desidera copiare gli elementi, utilizzare (ovvero una copia superficiale):

new_list = my_list[:]

Capiamo 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 annidati. Controlliamo cosa accadrà quando applichiamo la soluzione n. 1 alle liste annidate.

>>> 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

new_list = list(old_list)


Usa thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

Non sono sicuro se questo è ancora attuale, ma lo stesso comportamento vale anche per i dizionari. Guarda questo esempio.

a = {'par' : [1,21,3], 'sar' : [5,6,8]}
b = a
c = a.copy()
a['har'] = [1,2,3]

a
Out[14]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}

b
Out[15]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]}

c
Out[16]: {'par': [1, 21, 3], 'sar': [5, 6, 8]}

Python 3.6.0 Tempi

Ecco i risultati dei tempi usando Python 3.6.0. Tieni presente che questi tempi sono relativi tra loro, non assoluti.

Mi sono limitato a fare solo copie poco profonde e ho aggiunto anche alcuni nuovi metodi che non erano possibili in Python2, come list.copy() (l' equivalente della slice Python3) e list *new_list, = list ( *new_list, = list ):

METHOD                  TIME TAKEN
b = a[:]                6.468942025996512   #Python2 winner
b = a.copy()            6.986593422974693   #Python3 "slice equivalent"
b = []; b.extend(a)     7.309216841997113
b = a[0:len(a)]         10.916740721993847
*b, = a                 11.046738261007704
b = list(a)             11.761539687984623
b = [i for i in a]      24.66165203397395
b = copy.copy(a)        30.853400873980718
b = []
for item in a:
  b.append(item)        48.19176080400939

Possiamo vedere che il vecchio vincitore è ancora in cima, ma non in grande quantità, considerando la maggiore leggibilità dell'approccio python3 list.copy() .

Si noti che questi metodi non generano risultati equivalenti per qualsiasi input diverso dagli elenchi. Funzionano tutti per oggetti affettabili, alcuni funzionano per qualsiasi iterabile, ma solo copy.copy() funziona per qualsiasi oggetto Python.

Ecco il codice di prova 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\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", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\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 = []\nfor 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))

new_list = my_list[:]

new_list = my_list Cerca di capire questo. Diciamo che my_list è nella memoria heap nella posizione X cioè my_list punta alla X. Ora assegnando new_list = my_list stai Lasciando new_list puntando 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 Copy.

L'altro modo in cui puoi farlo sono:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_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, per 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

A differenza di altri linguaggi che hanno variabile e valore , Python ha nome e oggetto .

Questa dichiarazione:

a = [1,2,3]

significa dare alla lista (oggetto) un nome a , e, questo:

b = a

dà allo stesso oggetto a nuovo nome b , quindi ogni volta che fai qualcosa con a , l'oggetto cambia e quindi b cambia.

L'unico modo per fare veramente una copia di a è creare un nuovo oggetto come già hanno detto altre risposte.

Puoi vedere di più su questo here .


Tutti gli altri contributori hanno dato grandi risposte, che funzionano quando si ha una lista a una sola dimensione (livellata), tuttavia dei metodi menzionati finora, solo copy.deepcopy() funziona per clonare / copiare un elenco e non farlo puntare al list oggetti nidificati quando si lavora con elenchi nidificati e multidimensionali (elenco di elenchi). Mentre riferisce ad esso nella sua risposta, c'è un po 'di più nel problema e probabilmente una soluzione alternativa che utilizza i built-in che potrebbero rivelarsi un'alternativa più veloce alla deepcopy .

Mentre new_list = old_list[:] , copy.copy(old_list)' e per Py3k old_list.copy() funzionano per gli elenchi a livello singolo, tornano a puntare sugli oggetti list nidificati all'interno di old_list e new_list e cambiano in uno degli oggetti della list si perpetuano nell'altro.

Modifica: nuove informazioni portate alla luce

Come è stato sottolineato da e PM 2Ring utilizzando eval() non è solo una cattiva idea, è anche molto più lento di copy.deepcopy() .

Ciò significa che per gli elenchi multidimensionali, l'unica opzione è copy.deepcopy() . Detto questo, non è davvero un'opzione in quanto le prestazioni vanno verso sud quando si tenta di utilizzarlo su un array multidimensionale di dimensioni moderate. Ho provato a usare timeit usando un array 42x42, non sconosciuto o anche di grandi dimensioni per le applicazioni di bioinformatica, e ho rinunciato ad aspettare una risposta e ho appena iniziato a digitare la mia modifica su questo post.

Sembrerebbe che l'unica vera opzione sia quella di inizializzare più liste e lavorarci su in modo indipendente. Se qualcuno ha altri suggerimenti, per come gestire la copia di elenchi multidimensionali, sarebbe apprezzato.

Come altri hanno affermato, ci possono essere problemi di prestazioni significativi usando il modulo di copy e copy.deepcopy per gli elenchi multidimensionali . Cercando di elaborare un modo diverso di copiare la lista multidimensionale senza usare deepcopy , (stavo lavorando su un problema per un corso che consente solo 5 secondi per l'intero algoritmo di funzionare per ricevere credito), ho trovato un modo di utilizzare le funzioni integrate per creare una copia dell'elenco nidificato senza che siano puntate l'una sull'altra o sugli oggetti della list annidati al loro interno. Ho usato eval() e repr() nel compito per rendere la copia della vecchia lista nella nuova lista senza creare un collegamento alla vecchia lista. Si presenta sotto forma di:

new_list = eval(repr(old_list))

Fondamentalmente ciò che fa è rendere una rappresentazione di old_list come una stringa e quindi valuta la stringa come se fosse l'oggetto rappresentato dalla stringa. In questo modo, non viene creato alcun collegamento all'oggetto list originale. Viene creato un nuovo oggetto list e ogni variabile punta al proprio oggetto indipendente. Ecco un esempio che utilizza un elenco nidificato bidimensionale.

old_list = [[0 for j in range(y)] for i in range(x)] # initialize (x,y) nested list

# assign a copy of old_list to new list without them pointing to the same list object
new_list = eval(repr(old_list)) 

# make a change to new_list 
for j in range(y):
    for i in range(x):
    new_list[i][j] += 1

Se poi controlli il contenuto di ogni lista, per esempio un elenco 4 per 3, Python ritornerà

>>> new_list

[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

>>> old_list

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Anche se questo probabilmente non è il modo canonico o sintatticamente corretto per farlo, sembra funzionare bene. Non ho provato le prestazioni, ma ho intenzione di indovinare che eval() e rep() avranno meno overhead da eseguire rispetto a deepcopy .


Quali sono le opzioni per clonare o copiare un elenco in Python?

In Python 3, una copia superficiale può essere fatta con:

a_copy = a_list.copy()

In Python 2 e 3, puoi ottenere una copia superficiale con una porzione completa dell'originale:

a_copy = a_list[:]

Spiegazione

Ci sono due modi semantici per copiare una lista. Una copia superficiale crea un nuovo elenco degli stessi oggetti, una copia profonda crea un nuovo elenco contenente nuovi oggetti equivalenti.

Copia della lista ridotta

Una copia superficiale copia solo la lista stessa, che è un contenitore di riferimenti agli oggetti nella lista. Se gli oggetti contenuti sono mutabili e uno viene modificato, la modifica verrà riflessa in entrambe le liste.

Ci sono diversi modi per farlo in Python 2 e 3. I modi Python 2 funzioneranno anche in Python 3.

Python 2

In Python 2, il modo idiomatico di creare una copia superficiale di una lista è con una porzione completa dell'originale:

a_copy = a_list[:]

Puoi anche ottenere lo stesso risultato passando la lista attraverso il costruttore della lista,

a_copy = list(a_list)

ma usare il costruttore è meno efficiente:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

In Python 3, gli elenchi ottengono il metodo list.copy :

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

Fare un altro puntatore non fa una copia

Usando new_list = my_list modifica quindi new_list ogni volta che my_list cambia. Perchè è questo?

my_list è solo un nome che punta alla lista reale in memoria. Quando dici new_list = my_list non stai facendo una copia, stai solo aggiungendo un altro nome che punta a new_list = my_list originale in memoria. Possiamo avere problemi simili quando facciamo copie di elenchi.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

La lista è solo una serie di puntatori ai contenuti, quindi una copia superficiale copia solo i puntatori e quindi hai due elenchi diversi, ma hanno lo stesso contenuto. Per fare copie dei contenuti, è necessaria una copia profonda.

Copie profonde

Per fare una copia profonda di una lista, in Python 2 o 3, usa deepcopy nel modulo di copy :

import copy
a_deep_copy = copy.deepcopy(a_list)

Per dimostrare come questo ci permette di creare nuovi sotto-elenchi:

>>> 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 eseguire la tua funzione, ma non farlo. È probabile che crei bug che altrimenti non avresti utilizzando la funzione deepcopy della libreria standard.

Non usare eval

Potresti vedere questo usato come un modo per eseguire la deepcopy, ma non farlo:

problematic_deep_copy = eval(repr(a_list))
  1. È pericoloso, soprattutto se stai valutando qualcosa da una fonte di cui non ti fidi.
  2. Non è affidabile, se un sottoelemento che stai copiando non ha una rappresentazione che può essere valutata per riprodurre un elemento equivalente.
  3. È anche meno performante.

In Python 64 bit 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 Python 3.5 a 64 bit:

>>> 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

Iniziamo dall'inizio e esploriamolo un po 'in profondità:

Quindi supponiamo di avere due liste:

list_1=['01','98']
list_2=[['01','98']]

E dobbiamo copiare entrambe le liste, ora a partire dalla prima lista:

Quindi proviamo prima con il metodo generale di copia:

copy=list_1

Ora, se stai pensando di copiare la lista_1, puoi sbagliarti, controlliamola:

The id() function shows us that both variables point to the same list object, i.e. they share this object.
print(id(copy))
print(id(list_1))

produzione:

4329485320
4329485320

Sorpreso? Ok esploriamolo:

Quindi, poiché sappiamo che Python non memorizza nulla in una variabile, le variabili fanno semplicemente riferimento all'oggetto e all'archivio oggetti del valore. Qui object è list ma abbiamo creato due riferimenti a quello stesso oggetto con due diversi nomi di variabili. Quindi entrambe le variabili puntano allo stesso oggetto:

quindi quando fai copy=list_1 cosa fa in realtà:

Qui nell'immagine list_1 e la copia sono due nomi di variabili ma l'oggetto è lo stesso per entrambe le variabili che è list

Quindi, se provi a modificare l'elenco copiato, verrà modificato anche l'elenco originale perché l'elenco è solo uno lì, l'elenco verrà modificato 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:

Qual è la soluzione allora?

Soluzione:

Passiamo ora a un secondo metodo pithonic di lista di copia:

copy_1=list_1[:]

Ora questo metodo risolve ciò che stavamo affrontando nel primo numero controlliamolo:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

Quindi, come possiamo vedere entrambe le nostre liste con id differenti e significa che entrambe le variabili puntano a oggetti diversi, quindi quello che sta succedendo qui è:

Ora proviamo a modificare l'elenco e vediamo se dobbiamo ancora affrontare il problema precedente:

copy_1[0]="modify"

print(list_1)
print(copy_1)

Produzione:

['01', '98']
['modify', '98']

Quindi, come puoi vedere, non sta modificando l'elenco originale, ma ha modificato solo l'elenco copiato, quindi siamo d'accordo.

Quindi ora penso che abbiamo finito? aspettiamo che dobbiamo copiare anche la seconda lista nidificata, quindi proviamo a fare la pitonia:

copy_2=list_2[:]

Quindi list_2 dovrebbe fare riferimento a un altro oggetto che è la copia di list_2 controlliamo:

print(id((list_2)),id(copy_2))

otteniamo l'output:

4330403592 4330403528

Ora possiamo assumere che entrambe le liste puntino a oggetti diversi, quindi proviamo a modificarlo e vediamo che sta dando quello che vogliamo:

Quindi quando proviamo:

copy_2[0][1]="modify"

print(list_2,copy_2)

ci dà l'output:

[['01', 'modify']] [['01', 'modify']]

Ora, questo è un po 'di confusione, abbiamo usato il modo pititico e ancora, siamo di fronte allo stesso problema.

capiamolo:

Quindi quando lo facciamo:

copy_2=list_2[:]

in realtà stiamo copiando solo la lista esterna, non la lista nidificata, quindi la lista annidata è lo stesso oggetto per entrambi gli elenchi, controlliamo:

print(id(copy_2[0]))
print(id(list_2[0]))

produzione:

4329485832
4329485832

Quindi in realtà quando facciamo copy_2=list_2[:] questo è ciò che accade:

Crea la copia dell'elenco ma solo la copia dell'elenco esterno, non la copia dell'elenco annidato, l'elenco annidato è lo stesso per entrambe le variabili, quindi se provi a modificare l'elenco annidato, modificherà anche l'elenco originale perché l'oggetto elenco nidificato è lo stesso per entrambi lista annidata

Quindi qual è la soluzione?

La soluzione è deep copy

from copy import deepcopy
deep=deepcopy(list_2)

Quindi ora controlliamolo:

print(id((list_2)),id(deep))

produzione:

4322146056 4322148040

entrambi gli id ​​sono diversi, ora controlliamo l'id dell'elenco nidificato:

print(id(deep[0]))
print(id(list_2[0]))

produzione:

4322145992
4322145800

Come puoi vedere entrambi gli ID sono diversi, quindi possiamo supporre che entrambi gli elenchi annidati puntino ora a oggetti diversi.

Quindi quando fai deep=deepcopy(list_2) cosa succede realmente:

Quindi entrambe le liste annidate puntano a oggetti diversi e ora hanno una copia sepa- rata dell'elenco annidato.

Ora proviamo a modificare l'elenco annidato e vediamo se ha risolto il problema precedente o no:

quindi se lo facciamo:

deep[0][1]="modify"
print(list_2,deep)

produzione:

[['01', '98']] [['01', 'modify']]

Quindi, come puoi vedere, non ha modificato l'elenco annidato originale, ha solo modificato l'elenco copiato.

Se ti piace la mia risposta dettagliata, fammelo sapere svaligiandola, se hai qualche dubbio in merito a questa risposta, commenta :)


Con new_list = my_list , in realtà non hai due elenchi. L'assegnazione copia solo il riferimento alla lista, non la lista reale, quindi sia new_list che my_list riferiscono alla stessa lista dopo l'assegnazione.

Per copiare effettivamente l'elenco, hai varie possibilità:

  • Puoi usare il builtin list.copy() (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 sintassi strana e non ha senso usarla mai . ;) (Secondo lui, il prossimo è più leggibile).

  • Puoi usare la funzione built-in list() :

    new_list = list(old_list)
    
  • Puoi usare generici copy.copy() :

    import copy
    new_list = copy.copy(old_list)
    

    Questo è un po 'più lento di list() perché deve prima trovare il datatype di old_list .

  • Se la lista contiene oggetti e vuoi copiarli, usa generico copy.deepcopy() :

    import copy
    new_list = copy.deepcopy(old_list)
    

    Ovviamente il metodo più lento e che richiede più memoria, ma a volte inevitabile.

Esempio:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(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\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))

Risultato:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]

Sta restituendo un generatore. Non ho molta familiarità con Python, ma credo che sia lo stesso tipo di blocco iteratore di C # se hai familiarità con quelli.

L'idea chiave è che il compilatore / interprete / qualsiasi cosa faccia qualche trucco in modo tale che per quanto riguarda il chiamante, possono continuare a chiamare next () e manterrà i valori di ritorno - come se il metodo del generatore fosse sospeso . Ora ovviamente non si può realmente "mettere in pausa" un metodo, quindi il compilatore costruisce una macchina a stati per ricordare dove si è attualmente e quali sono le variabili locali ecc. Questo è molto più facile che scrivere un iteratore tu stesso.





python list copy clone