¿Cuáles son las opciones para clonar o copiar una lista en Python?
Durante el uso new_list = my_list
, cualquier modificación a los new_list
cambios my_list
cada vez. ¿Por qué es esto?
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)]
newlist = [*mylist]
también es una posibilidad en Python 3.newlist = list(mylist)
aunque quizás sea más claro.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[:]
ylist(list)
, a diferencia decopy.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 :)
fuente
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 alist(old_list)
, pero dado que es una sintaxis, no rutas de llamadas de función generales, ahorrará un poco en tiempo de ejecución (yold_list[:]
, a diferencia , que no escribe convert,[*old_list]
funciona en cualquier iterable y produce alist
).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 el
list.copy()
método, que debería ser tan rápido como cortar:newlist = old_list.copy()
fuente
s.copy()
crea una copia superficial des
(igual ques[:]
).python3.8
,.copy()
es un poco más rápido que cortar. Vea a continuación @AaronsHall respuesta.En Python 3, se puede hacer una copia superficial con:
En Python 2 y 3, puede obtener una copia superficial con una porción completa del original:
Explicación
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.
Copia de lista superficial
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.
Python 2
En Python 2, la forma idiomática de hacer una copia superficial de una lista es con una porción completa del original:
También puede lograr lo mismo pasando la lista a través del constructor de la lista,
pero usar el constructor es menos eficiente:
Python 3
En Python 3, las listas obtienen el
list.copy
método:En Python 3.5:
Hacer otro puntero no hace una copia
my_list
es solo un nombre que apunta a la lista real en la memoria. Cuando dicenew_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.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.
Copias profundas
Para hacer una copia profunda de una lista, en Python 2 o 3, use
deepcopy
en elcopy
módulo :Para demostrar cómo esto nos permite hacer nuevas sublistas:
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.
No usar
eval
Puede ver esto usado como una forma de hacer una copia profunda, pero no lo haga:
En Python 2.7 de 64 bits:
en Python 3.5 de 64 bits:
fuente
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 élmy_list
persistirán al referirse a él comonew_list
. Cada una de las otras respuestas a esta pregunta le brinda diferentes formas de crear un nuevo objeto al que vincularsenew_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.
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.
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.
Consulte la documentación para obtener más información sobre casos de esquina en la copia.
fuente
Utilizar
thing[:]
fuente
Comencemos desde el principio y exploremos esta pregunta.
Supongamos que tiene dos listas:
Y tenemos que copiar ambas listas, ahora comenzando desde la primera lista:
Entonces, primero intentemos estableciendo la variable
copy
en nuestra lista originallist_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:El resultado es:
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:
salida:
Entonces modificó la lista original:
Ahora pasemos a un método pitónico para copiar listas.
Este método soluciona el primer problema que tuvimos:
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:
El resultado es:
Como puede ver, solo modificó la lista copiada. Eso significa que funcionó.
¿Crees que hemos terminado? No. Intentemos copiar nuestra lista anidada.
list_2
debe hacer referencia a otro objeto que es copia delist_2
. Vamos a revisar:Obtenemos la salida:
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:
Esto nos da la salida:
Esto puede parecer un poco confuso, porque el mismo método que utilizamos anteriormente funcionó. Tratemos de entender esto.
Cuando tu lo hagas:
Solo está copiando la lista externa, no la lista interna. Podemos usar la
id
función una vez más para verificar esto.El resultado es:
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.Vamos a ver esto:
Ambas listas externas tienen ID diferentes, intentemos esto en las listas anidadas internas.
El resultado es:
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:
Produce:
Como puede ver, no modificó la lista anidada original, solo modificó la lista copiada.
fuente
El idioma de Python para hacer esto es
newList = oldList[:]
fuente
Python 3.6 Tiempos
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
ynew_list = [*list]
):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í ):
fuente
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 loslist
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ápidadeepcopy
.Mientras
new_list = old_list[:]
,copy.copy(old_list)'
y para Py3kold_list.copy()
funcionan para listas de un solo nivel, vuelven a apuntar a loslist
objetos anidados dentro deold_list
ynew_list
, y los cambios en uno de loslist
objetos se perpetúan en el otro.Editar: Nueva información sacada a la luz
Como han dicho otros, existen problemas importantes de rendimiento al usar el
copy
módulo ycopy.deepcopy
para las listas multidimensionales .fuente
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 usoeval()
, 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 deinput()
,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.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.
fuente
old_list
ynew_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):
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:
. Bonificación : si no desea copiar elementos, use (también conocido como copia superficial):
Comprendamos la diferencia entre la Solución n. ° 1 y la Solución n. ° 2
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.
fuente
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()
ocopy.deepcopy()
no las alternativas, por ejemplo, en Python 3:Salidas:
fuente
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)
fuente
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)
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:
Utiliza uno nuevo para las listas:
Y deepcopy_list tiene este aspecto:
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.
fuente
Una ligera perspectiva práctica para mirar en la memoria a través de id y gc.
fuente
Recuerda eso en Python cuando lo haces:
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.fuente
La opción de copia profunda es el único método que funciona para mí:
conduce a la salida de:
fuente
Esto se debe a que la línea
new_list = my_list
asigna una nueva referencia a la variablemy_list
quenew_list
es similar alC
código que se muestra a continuación,Debe usar el módulo de copia para crear una nueva lista por
fuente