ProgramaciónDesarrollador Backend Python

¿Cuál es la diferencia entre copia superficial y simple asignación de una variable? ¿Cómo funciona copy.copy() en relación con estructuras de datos anidadas (listas dentro de listas)?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

La asignación de una variable (por ejemplo, a = b) en Python no copia el objeto en sí, sino que simplemente crea un nuevo nombre (referencia) para el objeto existente. Los cambios a través de un nombre serán visibles a través del otro si se trata de un objeto mutable.

Copia superficial (shallow copy), por ejemplo a través de copy.copy(obj) o un corte [:] para una lista, crea un nuevo objeto de nivel superior, pero los objetos anidados dentro se copian por referencia (es decir, ambas estructuras "apuntan" a los mismos subcontenedores). Si se modifican los objetos anidados, los cambios serán visibles a través de ambos objetos.

Ejemplo:

import copy lst1 = [[1,2], [3,4]] lst2 = copy.copy(lst1) # o lst1[:] lst1[0][0] = 100 print(lst2) # [[100, 2], [3, 4]]

lst2 es una nueva lista, pero su primer elemento es la misma lista anidada.

Pregunta capciosa

Pregunta: ¿Cuál es la diferencia entre lst2 = lst1[:] y lst2 = copy.copy(lst1)?

Respuesta: En la práctica, para listas normales (de un solo nivel), no hay diferencia: ambos métodos realizan una copia superficial de la lista. Sin embargo, para clases de contenedores personalizados, puede haber un comportamiento diferente (por ejemplo, si se implementa su propio método __copy__). De la misma manera, para otros tipos (dict, set, etc.) es más seguro utilizar el módulo especializado copy.

import copy lst1 = [1, 2, 3] lst2 = lst1[:] lst3 = copy.copy(lst1) print(lst2 == lst3) # True

Ejemplos de errores reales debido a la falta de conocimiento sobre las sutilezas del tema


Historia

En un proyecto de manejo de configuraciones, un desarrollador duplicó los parámetros predeterminados a través de la asignación params = default_params y pensó que podía cambiarlos "aisladamente". Al final, cualquier cambio en cualquier copia llevaba a un efecto cascada en todas las partes de la aplicación, ya que en realidad se trabajaba con un solo objeto.


Historia

Un programador inexperto usó una copia superficial de la lista para almacenar el estado del juego (game_states = states[:]). Con una estructura anidada (listas de figuras en el campo), los cambios dentro de un estado "filtraban" en otros, rompiendo la historia de retrocesos y repeticiones de movimientos.


Historia

Al intentar clonar datos en una aplicación orientada a objetos, la elección estaba entre un corte y copy.copy(). Pero en la estructura se encontró con su propia clase con su propio método de copia, que solo fue considerado al usar copy.copy(). El corte ignoró la lógica de copia del objeto, y surgieron errores no evidentes.