Copia profunda de un dict en python


341

Me gustaría hacer una copia profunda de una dicten Python. Lamentablemente, el .deepcopy()método no existe para el dict. ¿Cómo puedo hacer eso?

>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = my_dict.deepcopy()
Traceback (most recent calll last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'deepcopy'
>>> my_copy = my_dict.copy()
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
7

La última línea debería ser 3.

Me gustaría que las modificaciones my_dictno afecten la instantánea my_copy.

¿Cómo puedo hacer eso? La solución debería ser compatible con Python 3.x.


3
No sé si es un duplicado, pero esto: stackoverflow.com/questions/838642/python-dictionary-deepcopy está muy cerca.
charleslparker

Respuestas:


473

Qué tal si:

import copy
d = { ... }
d2 = copy.deepcopy(d)

Python 2 o 3:

Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import copy
>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = copy.deepcopy(my_dict)
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
3
>>>

16
De hecho, eso funciona para el ejemplo demasiado simplificado que di. Mis claves no son números sino objetos. Si leo la documentación del módulo de copia, tengo que declarar un método __copy __ () / __ deepcopy __ () para las claves. ¡Muchas gracias por llevarme allí!
Olivier Grégoire

3
¿Hay alguna diferencia en los códigos Python 3.2 y 2.7? Me parecen idénticos. Si es así, sería mejor un solo bloque de código y una declaración "Funciona tanto para Python 3 como para 2"
MestreLion

30
También vale la pena mencionar copy.deepcopyque no es seguro para subprocesos. Aprendí esto de la manera difícil. Por otro lado, dependiendo de su caso de uso, json.loads(json.dumps(d)) es seguro para subprocesos y funciona bien.
robar

1
@rob deberías publicar ese comentario como respuesta. Es una alternativa viable. El matiz de seguridad del hilo es una distinción importante.
BuvinJ

3
@BuvinJ El problema es que json.loadsno resuelve el problema para todos los casos de uso donde los dictatributos de Python no son serializables JSON. Puede ayudar a aquellos que solo están lidiando con estructuras de datos simples, desde una API, por ejemplo, pero no creo que sea una solución suficiente para responder completamente la pregunta del OP.
robar

36

dict.copy () es una función de copia superficial para la
identificación del diccionario, es una función incorporada que le da la dirección de la variable

Primero debe comprender "¿por qué está ocurriendo este problema en particular?"

In [1]: my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}

In [2]: my_copy = my_dict.copy()

In [3]: id(my_dict)
Out[3]: 140190444167808

In [4]: id(my_copy)
Out[4]: 140190444170328

In [5]: id(my_copy['a'])
Out[5]: 140190444024104

In [6]: id(my_dict['a'])
Out[6]: 140190444024104

La dirección de la lista presente en ambos dictados para la clave 'a' apunta a la misma ubicación.
Por lo tanto, cuando cambia el valor de la lista en my_dict, la lista en my_copy también cambia.


Solución para la estructura de datos mencionada en la pregunta:

In [7]: my_copy = {key: value[:] for key, value in my_dict.items()}

In [8]: id(my_copy['a'])
Out[8]: 140190444024176

O puede usar la copia profunda como se mencionó anteriormente.


44
Su solución no funciona para diccionarios anidados. La copia profunda es preferible por esa razón.
Charles Plager

2
@CharlesPlager De acuerdo! Pero también debe notar que el corte de listas no funciona en dict value[:]. La solución fue para la estructura de datos particular mencionada en la pregunta en lugar de una solución universal.
theBuzzyCoder

17

Python 3.x

desde copia importación copia profunda

my_dict = {'one': 1, 'two': 2}
new_dict_deepcopy = deepcopy(my_dict)

Sin una copia profunda, no puedo eliminar el diccionario de nombres de host de mi diccionario de dominio.

Sin la copia profunda me sale el siguiente error:

"RuntimeError: dictionary changed size during iteration"

... cuando trato de eliminar el elemento deseado de mi diccionario dentro de otro diccionario.

import socket
import xml.etree.ElementTree as ET
from copy import deepcopy

dominio es un objeto de diccionario

def remove_hostname(domain, hostname):
    domain_copy = deepcopy(domain)
    for domains, hosts in domain_copy.items():
        for host, port in hosts.items():
           if host == hostname:
                del domain[domains][host]
    return domain

Ejemplo de salida: [orginal] domains = {'localdomain': {'localhost': {'all': '4000'}}}

[nuevos] dominios = {'localdomain': {}}}

Entonces, lo que está sucediendo aquí es que estoy iterando sobre una copia de un diccionario en lugar de iterar sobre el diccionario en sí. Con este método, puede eliminar elementos según sea necesario.


-3

Me gusta y aprendí mucho de Lasse V. Karlsen. Lo modifiqué en el siguiente ejemplo, que resalta bastante bien la diferencia entre las copias de diccionario poco profundas y las copias profundas:

    import copy

    my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
    my_copy = copy.copy(my_dict)
    my_deepcopy = copy.deepcopy(my_dict)

Ahora si cambias

    my_dict['a'][2] = 7

y hacer

    print("my_copy a[2]: ",my_copy['a'][2],",whereas my_deepcopy a[2]: ", my_deepcopy['a'][2])

usted obtiene

    >> my_copy a[2]:  7 ,whereas my_deepcopy a[2]:  3

1
¿Por qué crees que esta respuesta es diferente a la respuesta de Lasse V. Karlsen ? ¿Qué agrega que la otra respuesta no dice?
Olivier Grégoire

Hola olivier No estoy tratando de tomar el mérito de la respuesta de Lasse V. Karlsen : esencialmente resolvió el problema que tuve, y estoy en deuda con él. Mi comentario no es diferente, solo es complementario. Por la simple razón de que contrasta "copia" con "copia profunda". Esta fue la fuente de mi problema, porque me equivoqué al usarlos de manera equivalente. Salud.
Rafael Monteiro

-9

Una solución más simple (en mi opinión) es crear un nuevo diccionario y actualizarlo con el contenido del anterior:

my_dict={'a':1}

my_copy = {}

my_copy.update( my_dict )

my_dict['a']=2

my_dict['a']
Out[34]: 2

my_copy['a']
Out[35]: 1

El problema con este enfoque es que puede no ser "lo suficientemente profundo". es decir, no es recursivamente profundo. suficientemente bueno para objetos simples pero no para diccionarios anidados. Aquí hay un ejemplo donde puede no ser lo suficientemente profundo:

my_dict1={'b':2}

my_dict2={'c':3}

my_dict3={ 'b': my_dict1, 'c':my_dict2 }

my_copy = {}

my_copy.update( my_dict3 )

my_dict1['b']='z'

my_copy
Out[42]: {'b': {'b': 'z'}, 'c': {'c': 3}}

Al usar Deepcopy () puedo eliminar el comportamiento semi-superficial, pero creo que uno debe decidir qué enfoque es el adecuado para su aplicación. En la mayoría de los casos, es posible que no le importe, pero debe tener en cuenta las posibles dificultades ... ejemplo final:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

my_copy2
Out[46]: {'b': {'b': 'z'}, 'c': {'c': 3}}

12
Esto hace una copia superficial del dict, que no es lo que el interrogador estaba pidiendo. Los objetos que contiene no se copian ellos mismos. ¡Y una forma más fácil de copia superficial es my_dict.copy()!
Blckknght
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.