ProgramaciónDesarrollador Python (middleware, backend, pruebas)

¿Cómo implementar correctamente la copia de colecciones en Python, en qué casos copy() es insuficiente y por qué, y cuándo es necesario un deep copy?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta: En Python, la copia de objetos (especialmente colecciones — listas, diccionarios) es crítica: simplemente asignar a una variable crea una nueva referencia, no una copia. Los métodos especiales copy() y deepcopy() se introdujeron para evitar efectos secundarios indeseados al usar estructuras conjuntamente.

Problema: Al trabajar con colecciones anidadas (lista de listas, diccionario dentro de un diccionario), un simple copy() reproduce solo el contenedor, pero no los elementos internos. Esto puede llevar a errores difíciles de detectar: los cambios en un elemento anidado se reflejan en todas las "copias".

Solución: copy.copy() crea una copia superficial (shallow copy) — un nuevo contenedor de nivel superior, pero los objetos anidados permanecen los mismos. copy.deepcopy() copia recursivamente todos los objetos anidados.

Ejemplo de código:

import copy lst = [[1, 2], [3, 4]] shallow = copy.copy(lst) deep = copy.deepcopy(lst) lst[0][0] = 10 print(shallow) # [[10, 2], [3, 4]] — el objeto anidado cambió! print(deep) # [[1, 2], [3, 4]] — deepcopy conserva el estado original

Características clave:

  • La asignación simple copia solo la referencia, sin duplicado
  • .copy() (o copy.copy()) copia el contenedor "exterior", pero no los objetos anidados
  • .deepcopy() se utiliza para una independencia completa de las copias

Preguntas trampa.

¿Es suficiente a veces escribir lst2 = lst[:] para copiar una lista?

lst2 = lst[:] crea una copia superficial de la lista, pero los objetos anidados (por ejemplo, listas dentro de una lista) seguirán siendo los mismos. Para listas planas, esto es suficiente; para estructuras anidadas, no.

Ejemplo:

lst = [[1], [2]] lst2 = lst[:] lst[0][0] = 99 print(lst2) # [[99], [2]] — el elemento anidado cambió en ambas "copias"

¿Funciona el método .copy() igual para todas las colecciones?

No. Por ejemplo, dict.copy() funciona como una copia superficial, list.copy() apareció solo con Python 3.3, para set — existe set.copy(). Para objetos personalizados, el soporte de .copy() depende del método implementado.


¿Se puede depender de deepcopy en todas partes? ¿Es seguro y eficiente?

No. deepcopy es una operación costosa: puede causar problemas de rendimiento, especialmente en estructuras grandes, y también falla en objetos no copiables/cerrados o no estándar. Usa deepcopy solo cuando realmente se necesita una copia recursiva completamente independiente.


Errores comunes y anti-patrones

  • Uso de la asignación normal o .copy() en lugar de deepcopy para estructuras anidadas
  • Aplicación de deepcopy a todos los objetos en secuencia (ralentiza el programa)
  • Intento de copiar objetos que no soportan copias (por ejemplo, archivos, flujos)

Ejemplo de la vida real

Caso negativo

En pruebas se hizo una copia de "un diccionario de diccionarios" a través de dict.copy(). Como resultado, las correcciones en la estructura anidada para un usuario de repente afectaron a otras pruebas (los datos se mutaron globalmente).

Pros:

  • Rápido
  • Sencillo

Contras:

  • Errores poco evidentes, ruptura de la aislamiento del entorno

Caso positivo

Implementamos copy.deepcopy(), describimos en la documentación el nivel de anidamiento y las características de la estructura de cada objeto, evitamos deepcopy para grandes volúmenes, donde se podía hacer una copia "manual" por partes.

Pros:

  • Transparencia del entorno
  • Independencia de los objetos

Contras:

  • Requiere comprensión de la estructura
  • Mucho mayor uso de memoria y tiempo de procesamiento para estructuras grandes