Respuestas:
Con new_list = my_list
, en realidad no tienes dos listas. La asignación solo copia la referencia a la lista, no la lista real, por lo que ambas new_list
y se my_list
refieren a la misma lista después de la asignación.
Para copiar realmente la lista, tiene varias posibilidades:
Puede usar el list.copy()
método incorporado (disponible desde Python 3.3):
new_list = old_list.copy()
Puedes cortarlo:
new_list = old_list[:]
La opinión de Alex Martelli (al menos en 2007 ) sobre esto es que es una sintaxis extraña y no tiene sentido usarla nunca . ;) (En su opinión, el siguiente es más legible).
Puede usar la list()
función integrada:
new_list = list(old_list)
Puedes usar genérico copy.copy()
:
import copy
new_list = copy.copy(old_list)
Esto es un poco más lento que list()
porque tiene que averiguar old_list
primero el tipo de datos .
Si la lista contiene objetos y desea copiarlos también, use genérico copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Obviamente, el método más lento y que necesita más memoria, pero a veces inevitable.
Ejemplo:
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))
Resultado:
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 ya proporcionó una excelente respuesta, pero pensé que haría una comparación de velocidad de los diversos métodos:
copy.deepcopy(old_list)
Copy()
método de python puro copiando clases con deepcopyCopy()
método Python puro no copia clases (solo dictados / listas / tuplas)for item in old_list: new_list.append(item)
[i for i in old_list]
(una lista de comprensión )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
( corte de lista )Entonces, el más rápido es el corte de listas. Pero tenga en cuenta que copy.copy()
, list[:]
y list(list)
, a diferencia de copy.deepcopy()
la versión de Python, no copie ninguna lista, diccionario o instancia de clase en la lista, por lo que si los originales cambian, también cambiarán en la lista copiada y viceversa.
(Aquí está el script si alguien está interesado o quiere plantear algún problema :)
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
módulo. Además, no se puede concluir mucho de micro puntos de referencia arbitrarios como este.
[*old_list]
debería ser más o menos equivalente a list(old_list)
, pero dado que es una sintaxis, no rutas de llamadas de función generales, ahorrará un poco en tiempo de ejecución (y old_list[:]
, a diferencia , que no escribe convert, [*old_list]
funciona en cualquier iterable y produce a list
).
timeit
, 50m de carreras en lugar de 100k) ver stackoverflow.com/a/43220129/3745896
[*old_list]
realidad parece superar a casi cualquier otro método. (ver mi respuesta vinculada en comentarios anteriores)
Me han dicho que Python 3.3+ agrega ellist.copy()
método, que debería ser tan rápido como cortar:
newlist = old_list.copy()
s.copy()
crea una copia superficial de s
(igual que s[:]
).
python3.8
, .copy()
es un poco más rápido que cortar. Vea a continuación @AaronsHall respuesta.
¿Cuáles son las opciones para clonar o copiar una lista en Python?
En Python 3, se puede hacer una copia superficial con:
a_copy = a_list.copy()
En Python 2 y 3, puede obtener una copia superficial con una porción completa del original:
a_copy = a_list[:]
Hay dos formas semánticas para copiar una lista. Una copia superficial crea una nueva lista de los mismos objetos, una copia profunda crea una nueva lista que contiene nuevos objetos equivalentes.
Una copia superficial solo copia la lista misma, que es un contenedor de referencias a los objetos en la lista. Si los objetos contenidos en sí mismos son mutables y uno se modifica, el cambio se reflejará en ambas listas.
Hay diferentes formas de hacer esto en Python 2 y 3. Las formas de Python 2 también funcionarán en Python 3.
En Python 2, la forma idiomática de hacer una copia superficial de una lista es con una porción completa del original:
a_copy = a_list[:]
También puede lograr lo mismo pasando la lista a través del constructor de la lista,
a_copy = list(a_list)
pero usar el constructor es menos eficiente:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
En Python 3, las listas obtienen el list.copy
método:
a_copy = a_list.copy()
En 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
Usando new_list = my_list luego modifica new_list cada vez que my_list cambia. ¿Por qué es esto?
my_list
es solo un nombre que apunta a la lista real en la memoria. Cuando dice new_list = my_list
que no está haciendo una copia, simplemente está agregando otro nombre que apunta a esa lista original en la memoria. Podemos tener problemas similares cuando hacemos copias de las listas.
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
La lista es solo una matriz de punteros a los contenidos, por lo que una copia superficial simplemente copia los punteros, por lo que tiene dos listas diferentes, pero tienen el mismo contenido. Para hacer copias de los contenidos, necesita una copia profunda.
Para hacer una copia profunda de una lista, en Python 2 o 3, use deepcopy
en el copy
módulo :
import copy
a_deep_copy = copy.deepcopy(a_list)
Para demostrar cómo esto nos permite hacer nuevas sublistas:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
Y así vemos que la lista copiada en profundidad es una lista completamente diferente de la original. Podrías rodar tu propia función, pero no lo hagas. Es probable que cree errores que de otro modo no tendría utilizando la función de copia profunda de la biblioteca estándar.
eval
Puede ver esto usado como una forma de hacer una copia profunda, pero no lo haga:
problematic_deep_copy = eval(repr(a_list))
En Python 2.7 de 64 bits:
>>> 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
en Python 3.5 de 64 bits:
>>> 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))
y es mucho más rápido.
Ya hay muchas respuestas que le dicen cómo hacer una copia adecuada, pero ninguna de ellas dice por qué falló su 'copia' original.
Python no almacena valores en variables; une nombres a objetos. Su asignación original tomó el objeto al que hace referencia my_list
y también lo vinculó new_list
. Independientemente del nombre que utilice, todavía hay una sola lista, por lo que los cambios realizados al referirse a él my_list
persistirán al referirse a él como new_list
. Cada una de las otras respuestas a esta pregunta le brinda diferentes formas de crear un nuevo objeto al que vincularse new_list
.
Cada elemento de una lista actúa como un nombre, ya que cada elemento se une no exclusivamente a un objeto. Una copia superficial crea una nueva lista cuyos elementos se unen a los mismos objetos que antes.
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]
Para llevar su copia de la lista un paso más allá, copie cada objeto al que hace referencia su lista y vincule esas copias de elementos a una nueva lista.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Esto aún no es una copia profunda, porque cada elemento de una lista puede referirse a otros objetos, al igual que la lista está vinculada a sus elementos. Para copiar recursivamente cada elemento de la lista, y luego cada uno de los objetos mencionados por cada elemento, y así sucesivamente: realice una copia profunda.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Consulte la documentación para obtener más información sobre casos de esquina en la copia.
Utilizar thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Comencemos desde el principio y exploremos esta pregunta.
Supongamos que tiene dos listas:
list_1=['01','98']
list_2=[['01','98']]
Y tenemos que copiar ambas listas, ahora comenzando desde la primera lista:
Entonces, primero intentemos estableciendo la variable copy
en nuestra lista original list_1
:
copy=list_1
Ahora, si está pensando que la copia copió la lista_1, entonces está equivocado. La id
función puede mostrarnos si dos variables pueden apuntar al mismo objeto. Intentemos esto:
print(id(copy))
print(id(list_1))
El resultado es:
4329485320
4329485320
Ambas variables son exactamente el mismo argumento. ¿Estás sorprendido?
Entonces, como sabemos, python no almacena nada en una variable, las variables solo hacen referencia al objeto y el objeto almacena el valor. Aquí el objeto es un list
pero creamos dos referencias a ese mismo objeto por dos nombres de variables diferentes. Esto significa que ambas variables apuntan al mismo objeto, solo que con diferentes nombres.
Cuando lo haces copy=list_1
, en realidad está haciendo:
Aquí en la lista de imágenes_1 y la copia hay dos nombres de variables, pero el objeto es el mismo para ambas variables, que es list
Entonces, si intenta modificar la lista copiada, también modificará la lista original porque la lista es solo una allí, la modificará sin importar lo que haga desde la lista copiada o desde la lista original:
copy[0]="modify"
print(copy)
print(list_1)
salida:
['modify', '98']
['modify', '98']
Entonces modificó la lista original:
Ahora pasemos a un método pitónico para copiar listas.
copy_1=list_1[:]
Este método soluciona el primer problema que tuvimos:
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
Entonces, como podemos ver, nuestra lista tiene una identificación diferente y significa que ambas variables están apuntando a objetos diferentes. Entonces, lo que realmente está sucediendo aquí es:
Ahora intentemos modificar la lista y veamos si aún enfrentamos el problema anterior:
copy_1[0]="modify"
print(list_1)
print(copy_1)
El resultado es:
['01', '98']
['modify', '98']
Como puede ver, solo modificó la lista copiada. Eso significa que funcionó.
¿Crees que hemos terminado? No. Intentemos copiar nuestra lista anidada.
copy_2=list_2[:]
list_2
debe hacer referencia a otro objeto que es copia de list_2
. Vamos a revisar:
print(id((list_2)),id(copy_2))
Obtenemos la salida:
4330403592 4330403528
Ahora podemos suponer que ambas listas apuntan a un objeto diferente, así que ahora tratemos de modificarlo y veamos que está dando lo que queremos:
copy_2[0][1]="modify"
print(list_2,copy_2)
Esto nos da la salida:
[['01', 'modify']] [['01', 'modify']]
Esto puede parecer un poco confuso, porque el mismo método que utilizamos anteriormente funcionó. Tratemos de entender esto.
Cuando tu lo hagas:
copy_2=list_2[:]
Solo está copiando la lista externa, no la lista interna. Podemos usar la id
función una vez más para verificar esto.
print(id(copy_2[0]))
print(id(list_2[0]))
El resultado es:
4329485832
4329485832
Cuando lo hacemos copy_2=list_2[:]
, esto sucede:
Crea la copia de la lista, pero solo la copia de la lista externa, no la copia de la lista anidada, la lista anidada es la misma para ambas variables, por lo que si intenta modificar la lista anidada, también modificará la lista original ya que el objeto de la lista anidada es el mismo para ambas listas
¿Cuál es la solución? La solución es la deepcopy
función.
from copy import deepcopy
deep=deepcopy(list_2)
Vamos a ver esto:
print(id((list_2)),id(deep))
4322146056 4322148040
Ambas listas externas tienen ID diferentes, intentemos esto en las listas anidadas internas.
print(id(deep[0]))
print(id(list_2[0]))
El resultado es:
4322145992
4322145800
Como puede ver, ambas ID son diferentes, lo que significa que podemos suponer que ambas listas anidadas apuntan a un objeto diferente ahora.
Esto significa que cuando haces deep=deepcopy(list_2)
lo que realmente sucede:
Ambas listas anidadas apuntan a un objeto diferente y ahora tienen una copia separada de la lista anidada.
Ahora intentemos modificar la lista anidada y ver si resolvió el problema anterior o no:
deep[0][1]="modify"
print(list_2,deep)
Produce:
[['01', '98']] [['01', 'modify']]
Como puede ver, no modificó la lista anidada original, solo modificó la lista copiada.
Aquí están los resultados de sincronización usando Python 3.6.8. Tenga en cuenta que estos tiempos son relativos entre sí, no absolutos.
Me limité a hacer solo copias superficiales, y también agregué algunos métodos nuevos que no eran posibles en Python2, como list.copy()
(el equivalente de corte de Python3 ) y dos formas de desempaquetar listas ( *new_list, = list
y 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
Podemos ver que el ganador de Python2 todavía lo hace bien, pero no supera list.copy()
mucho a Python3 , especialmente teniendo en cuenta la legibilidad superior de este último.
El caballo oscuro es el método de desempaque y reempaque ( b = [*a]
), que es ~ 25% más rápido que el corte en bruto, y más del doble de rápido que el otro método de desempaque ( *b, = a
).
b = a * 1
También lo hace sorprendentemente bien.
Tenga en cuenta que estos métodos no generan resultados equivalentes para ninguna entrada que no sean listas. Todos funcionan para objetos divisibles, algunos funcionan para cualquier iterable, pero solo copy.copy()
funcionan para objetos Python más generales.
Aquí está el código de prueba para las partes interesadas ( Plantilla de aquí ):
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]
, la única forma obvia de hacerlo;).
Todos los demás contribuyentes dieron excelentes respuestas, que funcionan cuando tiene una lista de una sola dimensión (nivelada), sin embargo, de los métodos mencionados hasta ahora, solo copy.deepcopy()
funciona para clonar / copiar una lista y no hacer que apunte a los list
objetos anidados cuando está trabajando con listas anidadas multidimensionales (lista de listas). Si bien Felix Kling se refiere a eso en su respuesta, hay un poco más sobre el problema y posiblemente una solución alternativa con el uso de elementos integrados que podrían ser una alternativa más rápida deepcopy
.
Mientras new_list = old_list[:]
, copy.copy(old_list)'
y para Py3k old_list.copy()
funcionan para listas de un solo nivel, vuelven a apuntar a los list
objetos anidados dentro de old_list
y new_list
, y los cambios en uno de los list
objetos se perpetúan en el otro.
Como lo señalaron Aaron Hall y PM 2Ring, el uso
eval()
no solo es una mala idea, sino que también es mucho más lentocopy.deepcopy()
.Esto significa que para las listas multidimensionales, la única opción es
copy.deepcopy()
. Dicho esto, realmente no es una opción, ya que el rendimiento va al sur cuando intentas usarlo en una matriz multidimensional de tamaño moderado. Traté detimeit
usar una matriz de 42x42, no tan desconocida o incluso tan grande para las aplicaciones de bioinformática, y dejé de esperar una respuesta y comencé a escribir mi edición en esta publicación.Parece que la única opción real es inicializar múltiples listas y trabajar en ellas de forma independiente. Si alguien tiene alguna otra sugerencia sobre cómo manejar la copia de listas multidimensionales, se agradecería.
Como han dicho otros, existen problemas importantes de rendimiento al usar el copy
módulo y copy.deepcopy
para las listas multidimensionales .
repr()
sea suficiente para volver a crear el objeto. Además, eval()
es una herramienta de último recurso; ver Eval realmente es peligroso por el veterano de SO Ned Batchelder para más detalles. Entonces, cuando defiende el uso eval()
, realmente debe mencionar que puede ser peligroso.
eval()
función en Python en general es un riesgo. No es tanto si hace uso de la función en el código o no, sino que es un agujero de seguridad en Python en sí mismo. Mi ejemplo no lo está utilizando con una función que recibe información de input()
, sys.agrv
o incluso un archivo de texto. Es más similar a la inicialización de una lista multidimensional en blanco una vez, y luego simplemente tener una forma de copiarla en un bucle en lugar de reinicializar en cada iteración del bucle.
new_list = eval(repr(old_list))
, por lo que, además de ser una mala idea, probablemente también sea demasiado lento para funcionar.
Me sorprende que esto no se haya mencionado aún, así que en aras de la integridad ...
Puede realizar el desempaquetado de la lista con el "operador splat": *
que también copiará elementos de su lista.
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
La desventaja obvia de este método es que solo está disponible en Python 3.5+.
Sin embargo, esto parece funcionar mejor que otros métodos comunes.
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
y new_list
hay dos listas diferentes, editar una no cambiará la otra (a menos que esté mutando directamente los elementos mismos (como la lista de la lista), ninguno de estos métodos son copias profundas).
Faltaba un enfoque muy simple independiente de la versión de Python en las respuestas ya dadas que puede usar la mayoría de las veces (al menos lo hago):
new_list = my_list * 1 #Solution 1 when you are not using nested lists
Sin embargo, si my_list contiene otros contenedores (por ejemplo, listas anidadas) debe usar la copia profunda como otros sugirieron en las respuestas anteriores de la biblioteca de copias. Por ejemplo:
import copy
new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists
. Bonificación : si no desea copiar elementos, use (también conocido como copia superficial):
new_list = my_list[:]
Comprendamos la diferencia entre la Solución n. ° 1 y la Solución 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])
Como puede ver, la Solución # 1 funcionó perfectamente cuando no estábamos usando las listas anidadas. Veamos qué sucederá cuando apliquemos la solución n. ° 1 a las listas anidadas.
>>> 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
Tenga en cuenta que hay algunos casos en los que si ha definido su propia clase personalizada y desea conservar los atributos, debería usar copy.copy()
o copy.deepcopy()
no las alternativas, por ejemplo, en 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))
Salidas:
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
Intenta entender esto. Digamos que my_list está en la memoria de almacenamiento dinámico en la ubicación X, es decir, my_list apunta a la X. Ahora asignandonew_list = my_list
que estás dejando que new_list apunte a la X. Esto se conoce como Copia superficial.
Ahora, si lo asigna, new_list = my_list[:]
simplemente está copiando cada objeto de my_list en new_list. Esto se conoce como copia profunda.
La otra forma en que puede hacer esto es:
new_list = list(old_list)
import copy
new_list = copy.deepcopy(old_list)
Quería publicar algo un poco diferente a algunas de las otras respuestas. Aunque es probable que esta no sea la opción más comprensible o más rápida, proporciona una visión interna de cómo funciona la copia profunda, además de ser otra opción alternativa para la copia profunda. Realmente no importa si mi función tiene errores, ya que el objetivo es mostrar una forma de copiar objetos como las respuestas a las preguntas, pero también usar esto como un punto para explicar cómo funciona la copia profunda en su núcleo.
El núcleo de cualquier función de copia profunda es la forma de hacer una copia superficial. ¿Cómo? Simple. Cualquier función de copia profunda solo duplica los contenedores de objetos inmutables. Cuando copia en profundidad una lista anidada, solo está duplicando las listas externas, no los objetos mutables dentro de las listas. Solo estás duplicando los contenedores. Lo mismo funciona para las clases, también. Cuando copia en profundidad una clase, copia en profundidad todos sus atributos mutables. ¿Así que cómo? ¿Cómo es que solo tiene que copiar los contenedores, como listas, dictados, tuplas, iters, clases e instancias de clase?
Es simple. Un objeto mutable no se puede duplicar realmente. Nunca se puede cambiar, por lo que es solo un valor único. Eso significa que nunca tendrá que duplicar cadenas, números, bools o ninguno de esos. ¿Pero cómo duplicaría los contenedores? Simple. Solo debe inicializar un nuevo contenedor con todos los valores. Deepcopy se basa en la recursividad. Duplica todos los contenedores, incluso aquellos con contenedores dentro de ellos, hasta que no queden contenedores. Un contenedor es un objeto inmutable.
Una vez que sepa eso, duplicar completamente un objeto sin ninguna referencia es bastante fácil. Aquí hay una función para realizar una copia en profundidad de los tipos de datos básicos (no funcionaría para clases personalizadas, pero siempre puede agregar eso)
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 copia profunda integrada de Python se basa en ese ejemplo. La única diferencia es que admite otros tipos, y también admite clases de usuario al duplicar los atributos en una nueva clase duplicada, y también bloquea la recursión infinita con una referencia a un objeto que ya se ha visto usando una lista de notas o un diccionario. Y eso es realmente para hacer copias profundas. En esencia, hacer una copia profunda es solo hacer copias superficiales. Espero que esta respuesta agregue algo a la pregunta.
EJEMPLOS
Digamos que tiene esta lista: [1, 2, 3] . Los números inmutables no pueden duplicarse, pero la otra capa sí. Puede duplicarlo utilizando una lista de comprensión: [x para x en [1, 2, 3]
Ahora, imagine que tiene esta lista: [[1, 2], [3, 4], [5, 6]] . Esta vez, desea hacer una función, que utiliza la recursividad para copiar en profundidad todas las capas de la lista. En lugar de la comprensión de la lista anterior:
[x for x in _list]
Utiliza uno nuevo para las listas:
[deepcopy_list(x) for x in _list]
Y deepcopy_list tiene este aspecto:
def deepcopy_list(x):
if isinstance(x, (str, bool, float, int)):
return x
else:
return [deepcopy_list(y) for y in x]
Entonces ahora tiene una función que puede hacer una copia en profundidad de cualquier lista de strs, bools, floast, ints e incluso listas de infinitas capas usando recursividad. Y ahí lo tienes, copiando en profundidad.
TLDR : Deepcopy utiliza la recursividad para duplicar objetos, y simplemente devuelve los mismos objetos inmutables que antes, ya que los objetos inmutables no pueden duplicarse. Sin embargo, copia en profundidad las capas más internas de objetos mutables hasta que alcanza la capa mutable más externa de un objeto.
Una ligera perspectiva práctica para mirar en la memoria a través de id y 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)
Recuerda eso en Python cuando lo haces:
list1 = ['apples','bananas','pineapples']
list2 = list1
List2 no almacena la lista real, sino una referencia a list1. Entonces, cuando haces algo para list1, list2 también cambia. use el módulo de copia (no predeterminado, descarga en pip) para hacer una copia original de la lista ( copy.copy()
para listas simples, copy.deepcopy()
para las anidadas). Esto hace una copia que no cambia con la primera lista.
La opción de copia profunda es el único método que funciona para mí:
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('-----------------------------')
conduce a la salida de:
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]]]
-----------------------------
Esto se debe a que la línea new_list = my_list
asigna una nueva referencia a la variable my_list
que new_list
es similar al C
código que se muestra a continuación,
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
Debe usar el módulo de copia para crear una nueva lista por
import copy
new_list = copy.deepcopy(my_list)
newlist = [*mylist]
también es una posibilidad en Python 3.newlist = list(mylist)
aunque quizás sea más claro.