¿Por qué actualizar el diccionario de copia “superficial” no actualiza el diccionario “original”? [duplicate]

5 minutos de lectura

avatar de usuario de user225312
usuario225312

Mientras leía la documentación de dict.copy(), dice que hace una copia superficial del diccionario. Lo mismo ocurre con el libro que estoy siguiendo (Beazley’s Python Reference), que dice:

El método m.copy() hace una copia superficial de los elementos contenidos en un objeto de mapeo y los coloca en un nuevo objeto de mapeo.

Considera esto:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

Así que asumí que esto actualizaría el valor de original (y agregue ‘c’: 3) también porque estaba haciendo una copia superficial. Like si lo haces por una lista:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

Esto funciona como se esperaba.

Dado que ambos son copias superficiales, ¿por qué es que el dict.copy() no funciona como esperaba? ¿O mi comprensión de la copia superficial frente a la profunda es defectuosa?

  • Pintoresco que no explican “superficial”. Conocimiento interno, guiño. Solo el dictado y las claves son una copia, mientras que los dictados anidados dentro de ese primer nivel son referencias, no se pueden eliminar en un bucle, por ejemplo. Por lo tanto, dict.copy() de Python en ese caso no es útil ni intuitivo. Gracias por tu pregunta.

    – gseattle

    9 de junio de 2017 a las 10:34


  • “Dado que ambas son copias superficiales”, ese es el problema aquí. el segundo ejemplo no es una copia, superficial o no.

    – Karl Knechtel

    hace 2 días


avatar de usuario de kennytm
kennytm

Por “copia superficial” se entiende la contenido del diccionario no se copia por valor, sino simplemente creando una nueva referencia.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

Por el contrario, una copia profunda copiará todos los contenidos por valor.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

Entonces:

  1. b = a: Asignación de referencia, Marca a y b apunta al mismo objeto.

    Ilustración de 'a = b': 'a' y 'b' apuntan a '{1: L}', 'L' apunta a '[1, 2, 3]'.

  2. b = a.copy(): copia superficial, a y b se convertirán en dos objetos aislados, pero sus contenidos seguirán compartiendo la misma referencia

    Ilustración de 'b = a.copy()': 'a' apunta a '{1: L}', 'b' apunta a '{1: M}', 'L' y 'M' apuntan a '[1, 2, 3]'.

  3. b = copy.deepcopy(a): copia profunda, a y bLa estructura y el contenido de quedan completamente aislados.

    Ilustración de 'b = copy.deepcopy(a)': 'a' apunta a '{1: L}', 'L' apunta a '[1, 2, 3]';  'b' apunta a '{1: M}', 'M' apunta a una instancia diferente de '[1, 2, 3]'.

  • Buena respuesta, pero podría considerar corregir el error gramatical en su primera oración. Y no hay razón para no usar L de nuevo en b. Hacerlo simplificaría el ejemplo.

    –Tom Russell

    27 de octubre de 2017 a las 4:47


  • @kennytm: ¿Cuál es la diferencia entre los dos primeros ejemplos, de hecho? Obtiene el mismo resultado, pero una implementación interna ligeramente diferente, pero ¿qué importa?

    – Java Sa

    20 de enero de 2018 a las 11:53


  • @JavaSa a[1] = {} afectará b en el primer ejemplo, y no afectará b en el segundo ejemplo.

    – kennytm

    22 de enero de 2018 a las 18:08

  • Gran explicación,… ¡realmente me salvó el día! Gracias … ¿Se puede aplicar esto mismo a la lista, str y otros tipos de datos de python?

    – Bhuro

    19 de febrero de 2018 a las 13:38

  • @Sheldore dic_a y dic_b están haciendo referencia al mismo diccionario cuando dic_b = dic_a. Las cadenas en el diccionario son inmutables pero eso es irrelevante, porque dic_b['A'] = 'Adam' muta el propio diccionario.

    – kennytm

    20 de septiembre de 2020 a las 17:54

Toma este ejemplo:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

Ahora cambiemos un valor en el nivel ‘superficial’ (primero):

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

Ahora cambiemos un valor un nivel más profundo:

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed

  • no change in original, since ['a'] is an immutable integer Este. En realidad responde a la pregunta formulada.

    – CivFan

    14 de abril de 2015 a las 18:08


No es una cuestión de copia profunda o copia superficial, nada de lo que estás haciendo es una copia profunda.

Aquí:

>>> new = original 

está creando una nueva referencia a la lista/dictado al que hace referencia el original.

mientras aquí:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

está creando una nueva lista/dictado que se completa con una copia de las referencias de los objetos contenidos en el contenedor original.

  • Cierto, hasta donde llega… pero no va lo suficientemente lejos como para responder la pregunta OP sobre lo superficial frente a lo profundo.

    – MarkHu

    11 de febrero de 2021 a las 2:02

Avatar de usuario de Vkreddy
Vkreddy

Agregando a la respuesta de kennytm. Cuando haces una copia superficial padre.copia() se crea un nuevo diccionario con las mismas claves, pero los valores no se copian, se hace referencia a ellos. Si agrega un nuevo valor a parent_copy no tendrá efecto padre porque parent_copy es un nuevo diccionario no de referencia.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

El valor hash (id) de padre[1], parent_copy[1] son idénticos lo que implica [1,2,3] de padre[1] y parent_copy[1] almacenado en id 140690938288400.

Pero hash de padre y parent_copy son diferentes lo que implica que son diferentes diccionarios y parent_copy es un nuevo diccionario que tiene valores de referencia a valores de padre

“nuevo” y “original” son dictados diferentes, es por eso que puede actualizar solo uno de ellos. elementos son copias superficiales, no el dictado en sí.

Avatar de usuario de Stephen Rauch
Esteban Rauch

En tu segunda parte, deberías usar new = original.copy()

.copy y = son cosas diferentes.

Avatar de usuario de Jungle Hunter
Cazador de la selva

Contenido se copian superficialmente.

Entonces si el original dict contiene una list u otro dictionarymodificándolos en el original o en su copia superficial los modificará (los list o el dict) en el otro.

¿Ha sido útil esta solución?