Lista de copias de listas en Python
Esta es una de las más de 100 recetas gratuitas del libro IPython Cookbook, Second Edition, de Cyrille Rossant, una guía de computación numérica y ciencia de datos en el Jupyter Notebook. El libro electrónico y el libro impreso están disponibles para su compra en Packt Publishing.
Con NumPy se pueden conseguir importantes aumentos de velocidad en el rendimiento con respecto al código nativo de Python, especialmente cuando nuestros cálculos siguen el paradigma de instrucción única, datos múltiples (SIMD). Sin embargo, también es posible escribir involuntariamente código no optimizado con NumPy.
En las próximas recetas, veremos algunos trucos que pueden ayudarnos a escribir código NumPy optimizado. En esta receta, veremos cómo evitar copias innecesarias de arrays para ahorrar memoria. Para ello, necesitaremos indagar en las interioridades de NumPy.
Dos arrays con la misma ubicación de datos (devuelta por aid()) comparten el mismo buffer de datos subyacente. Sin embargo, lo contrario sólo es cierto si las matrices tienen el mismo desplazamiento (lo que significa que tienen el mismo primer elemento). Dos matrices compartidas con diferentes desplazamientos tendrán ubicaciones de memoria ligeramente diferentes, como se muestra en el siguiente ejemplo:
¿Cómo se copia un array en Python?
Podemos crear una copia de un array utilizando el operador de asignación (=). En Python, las sentencias de asignación no copian objetos, sino que crean enlaces entre un objetivo y un objeto.
¿Cómo se copia en profundidad un array en Python?
Para realizar una copia profunda, utilice la función deepcopy() del módulo de copia. En una copia profunda, se insertan copias en lugar de referencias a los objetos, por lo que el cambio de una no modifica la otra. A continuación se muestra un ejemplo de aplicación de la función deepcopy() a un slice.
Copia profunda de listas en Python
Para los objetos mutables como las listas y los diccionarios (= elementos de una lista o valores de un diccionario), la copia superficial inserta referencias, y la copia profunda inserta copias. En el caso de las referencias, se trata del mismo objeto, por lo que si uno de ellos se modifica, el otro también se modifica.
En cambio, en el caso de los objetos inmutables, como los números int, float y las cadenas de caracteres, el valor del objeto no se puede actualizar. Cuando se asignan, las dos variables son el mismo objeto, pero cuando una se actualiza a un nuevo valor, se convierte en un objeto diferente, y la otra sigue siendo la misma.
Como se ha mencionado anteriormente, una copia superficial inserta una referencia a un objeto en el objeto original. Por ejemplo, en el caso de una copia superficial de una lista, la propia lista es un objeto diferente, pero sus elementos son referencias a los mismos objetos de los elementos de la lista original.
Por lo tanto, si los elementos son mutables, cuando uno se actualiza, el otro también se actualiza. En el caso de una inmutable, cuando se actualiza a un nuevo valor, se convierte en un objeto diferente, y el otro sigue siendo el original.
Copia de Python
Copiar un elemento de otro es una operación cuidadosa que, si se realiza mal, tiene consecuencias. En programación, no sólo se copian valores; a veces, también se copian referencias. Veamos cómo copiar un array en Python.
Para copiar un array en Python, utiliza el operador de asignación(=). El operador de asignación no copia objetos. En su lugar, crea enlaces entre un objetivo y un objeto. Cuando usamos el operador =, pensamos que crea un nuevo objeto, pero no es así. En cambio, crea una nueva variable que comparte la referencia del objeto original.
Hemos copiado el arr2 de arr, pero ambos objetos del array comparten la misma referencia. Así que cuando modificas el array original, el cambio también se aplicará al array copiado. Así, si cambias el arr, el arr2 también cambiará.
Una copia superficial es una copia a nivel de bits del objeto. Significa que cuando se crea un nuevo objeto basado en el objeto original, éste tiene una copia exacta de los valores del objeto original. Si alguno de los valores del objeto son referencias a otros objetos, sólo se copian las direcciones de referencia.
Variable de copia en Python
nueva_lista = mi_lista no crea realmente una segunda lista. La asignación sólo copia la referencia a la lista, no la lista real, por lo que tanto nueva_lista como mi_lista se refieren a la misma lista después de la asignación.
Así que lo más rápido es el corte de la lista. Pero ten en cuenta que copy.copy(), list[:] y list(list), a diferencia de copy.deepcopy() y la versión de python no copian las listas, diccionarios e instancias de clase en la lista, por lo que si los originales cambian, también lo harán en la lista copiada y viceversa.
Una copia superficial sólo copia la propia lista, que es un contenedor de referencias a los objetos de la lista. Si los propios objetos contenidos son mutables y uno de ellos cambia, el cambio se reflejará en ambas listas.
mi_lista es sólo un nombre que apunta a la lista real en la memoria. Cuando dices nueva_lista = mi_lista no estás haciendo una copia, sólo estás añadiendo otro nombre que apunta a esa lista original en memoria. Podemos tener problemas similares cuando hacemos copias de listas.
La lista es sólo un array de punteros a los contenidos, por lo que una copia superficial sólo copia los punteros, y así tienes dos listas diferentes, pero con el mismo contenido. Para hacer copias del contenido, necesitas una copia profunda.