Cómo copiar un diccionario y solo editar la copia.

¿Puede alguien explicarme esto por favor? Esto no tiene ningún sentido para mí.

Copio un diccionario en otro y edito el segundo y ambos se cambian. ¿Por qué está pasando esto?

>>> dict1 = {"key1": "value1", "key2": "value2"} >>> dict2 = dict1 >>> dict2 {'key2': 'value2', 'key1': 'value1'} >>> dict2["key2"] = "WHY?!" >>> dict1 {'key2': 'WHY?!', 'key1': 'value1'} 

Python nunca copia implícitamente objetos. Cuando establece dict2 = dict1 , hace que se refieran al mismo objeto de dictado exacto, de modo que cuando lo muta, todas las referencias a él se refieren al objeto en su estado actual.

Si desea copiar el dict (lo cual es raro), debe hacerlo explícitamente con

 dict2 = dict(dict1) 

o

 dict2 = dict1.copy() 

Cuando asigna dict2 = dict1 , no está haciendo una copia de dict1 , lo que hace que dict2 sea ​​solo otro nombre para dict1 .

Para copiar los tipos mutables como los diccionarios, use copy / deepcopy del módulo copy .

 import copy dict2 = copy.deepcopy(dict1) 
 >>> x={'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3} >>> u=x.copy() >>> v=dict(x) >>> import copy >>> w=copy.deepcopy(x) >>> x['a']=10 >>> x {'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> u {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> v {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> w {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} >>> x['b']['m']=40 >>> x {'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> u {'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> v {'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}} >>> w {'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}} 

En Python 3.5+, hay una forma más fácil de lograr una copia superficial utilizando el operador de desempaquetado **. Definido por Pep 448 .

 >>>dict1 = {"key1": "value1", "key2": "value2"} >>>dict2 = {**dict1} >>>print(dict2) {'key1': 'value1', 'key2': 'value2'} >>>dict2["key2"] = "WHY?!" >>>print(dict1) {'key1': 'value1', 'key2': 'value2'} >>>print(dict2) {'key1': 'value1', 'key2': 'WHY?!'} 

** desempaqueta el diccionario en un diccionario nuevo que luego se asigna a dict2.

También podemos confirmar que cada diccionario tiene una identificación distinta.

 >>>id(dict1) 178192816 >>>id(dict2) 178192600 

Si se necesita una copia profunda, copy.deepcopy () sigue siendo el camino a seguir.

También puede simplemente hacer un nuevo diccionario con una comprensión del diccionario. Esto evita la importación de copia.

 dout = dict((k,v) for k,v in mydict.items()) 

Por supuesto en python> = 2.7 puedes hacer:

 dout = {k:v for k,v in mydict.items()} 

Pero para compatibilidades hacia atrás, el método superior es mejor.

Las mejores y más fáciles formas de crear una copia de un dict tanto en Python 2.7 como en 3 son …

Para crear una copia del diccionario simple (de un solo nivel):

1. Usar el método dict () , en lugar de generar una referencia que apunte al dict existente.

 my_dict1 = dict() my_dict1["message"] = "Hello Python" print(my_dict1) # {'message':'Hello Python'} my_dict2 = dict(my_dict1) print(my_dict2) # {'message':'Hello Python'} # Made changes in my_dict1 my_dict1["name"] = "Emrit" print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'} print(my_dict2) # {'message':'Hello Python'} 

2. Usando el método de actualización incorporado () del diccionario de Python.

 my_dict2 = dict() my_dict2.update(my_dict1) print(my_dict2) # {'message':'Hello Python'} # Made changes in my_dict1 my_dict1["name"] = "Emrit" print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'} print(my_dict2) # {'message':'Hello Python'} 

Para crear una copia de diccionario nested o complejo:

Utilice el módulo de copia incorporado, que proporciona operaciones de copia superficial y profunda genéricas. Este módulo está presente en Python 2.7 y 3. *

 import copy my_dict2 = copy.deepcopy(my_dict1) 

Las declaraciones de asignación en Python no copian objetos, crean enlaces entre un objective y un objeto.

entonces, dict2 = dict1 , da como resultado otro enlace entre dict2 y el objeto al que se refiere dict1 .

Si desea copiar un dict, puede utilizar el copy module . El módulo de copia tiene dos interfaces:

 copy.copy(x) Return a shallow copy of x. copy.deepcopy(x) Return a deep copy of x. 

La diferencia entre copia superficial y profunda solo es relevante para objetos compuestos (objetos que contienen otros objetos, como listas o instancias de clase):

Una copia superficial construye un nuevo objeto compuesto y luego (en la medida de lo posible) inserta referencias en él a los objetos que se encuentran en el original.

Una copia profunda construye un nuevo objeto compuesto y luego, recursivamente, inserta copias en él de los objetos que se encuentran en el original.

Por ejemplo, en Python 2.7.9:

 >>> import copy >>> a = [1,2,3,4,['a', 'b']] >>> b = a >>> c = copy.copy(a) >>> d = copy.deepcopy(a) >>> a.append(5) >>> a[4].append('c') 

y el resultado es:

 >>> a [1, 2, 3, 4, ['a', 'b', 'c'], 5] >>> b [1, 2, 3, 4, ['a', 'b', 'c'], 5] >>> c [1, 2, 3, 4, ['a', 'b', 'c']] >>> d [1, 2, 3, 4, ['a', 'b']] 

Además de las otras soluciones proporcionadas, puede utilizar ** para integrar el diccionario en un diccionario vacío, por ejemplo,

shallow_copy_of_other_dict = {**other_dict} .

Ahora tendrás una copia “superficial” de other_dict .

Aplicado a tu ejemplo:

 >>> dict1 = {"key1": "value1", "key2": "value2"} >>> dict2 = {**dict1} >>> dict2 {'key1': 'value1', 'key2': 'value2'} >>> dict2["key2"] = "WHY?!" >>> dict1 {'key1': 'value1', 'key2': 'value2'} >>> 

Puntero: Diferencia entre copys poco profundos y profundos.

Puede copiar y editar la copia recién construida de una sola vez llamando al constructor dict con argumentos de palabras clave adicionales:

 >>> dict1 = {"key1": "value1", "key2": "value2"} >>> dict2 = dict(dict1, key2="WHY?!") >>> dict1 {'key2': 'value2', 'key1': 'value1'} >>> dict2 {'key2': 'WHY?!', 'key1': 'value1'} 

Esto me confundió también, inicialmente, porque venía de un fondo de C.

En C, una variable es una ubicación en la memoria con un tipo definido. La asignación a una variable copia los datos en la ubicación de memoria de la variable.

Pero en Python, las variables actúan más como punteros a objetos. Entonces, asignar una variable a otra no hace una copia, solo hace que el nombre de esa variable apunte al mismo objeto.

Cada variable en python (cosas como dict1 o str o __builtins__ es un puntero a algún “objeto” platónico oculto dentro de la máquina.

Si establece dict1 = dict2 , simplemente apunte dict1 al mismo objeto (o ubicación de memoria, o cualquier analogía que desee) como dict2 . Ahora, el objeto al que hace referencia dict1 es el mismo objeto al que hace referencia dict2 .

Puede comprobar: dict1 is dict2 debe ser True . Además, id(dict1) debería ser el mismo que id(dict2) .

Desea dict1 = copy(dict2) , o dict1 = deepcopy(dict2) .

¿La diferencia entre copy y copy deepcopy ? deepcopy se asegurará de que los elementos de dict2 (¿lo dict2 a una lista?) también sean copias.

No uso mucho la deepcopy ; por lo general, es una mala práctica escribir código que lo necesite (en mi opinión).

dict2 = dict1 no copia el diccionario. Simplemente le da al progtwigdor una segunda forma ( dict2 ) para referirse al mismo diccionario.

dict1 es un símbolo que hace referencia a un objeto de diccionario subyacente. Asignar dict1 a dict2 simplemente asigna la misma referencia. Cambiar el valor de una clave a través del símbolo dict2 cambia el objeto subyacente, que también afecta a dict1 . Esto es confuso.

Es mucho más fácil razonar acerca de valores inmutables que de referencias, así que haga copias siempre que sea posible:

 person = {'name': 'Mary', 'age': 25} one_year_later = {**person, 'age': 26} # does not mutate person dict 

Esto es sintácticamente lo mismo que:

 one_year_later = dict(person, age=26) 
 >>> dict2 = dict1 # dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1 

Hay muchas formas de copiar el objeto Dict, simplemente uso

 dict_1 = { 'a':1, 'b':2 } dict_2 = {} dict_2.update(dict_1) 

Debido a que Python funciona con referencia, entonces cuando hiciste dict2 = dict1 pasas una referencia a dict2, eso es lo mismo que dict1. Por lo tanto, cuando realiza un cambio en dict1 o dict2, cambia una referencia y ambos dictados. Lo siento si me equivoco algo en inglés.

Como han explicado otros, el dict incorporado no hace lo que usted quiere. Pero en Python2 (y probablemente 3 también) puede crear fácilmente una clase ValueDict que se copia con = para que pueda estar seguro de que el original no cambiará.

 class ValueDict(dict): def __ilshift__(self, args): result = ValueDict(self) if isinstance(args, dict): dict.update(result, args) else: dict.__setitem__(result, *args) return result # Pythonic LVALUE modification def __irshift__(self, args): result = ValueDict(self) dict.__delitem__(result, args) return result # Pythonic LVALUE modification def __setitem__(self, k, v): raise AttributeError, \ "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k) def __delitem__(self, k): raise AttributeError, \ "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k) def update(self, d2): raise AttributeError, \ "Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\"" # test d = ValueDict() d <<='apples', 5 d <<='pears', 8 print "d =", d e = d e <<='bananas', 1 print "e =", e print "d =", d d >>='pears' print "d =", d d <<={'blueberries': 2, 'watermelons': 315} print "d =", d print "e =", e print "e['bananas'] =", e['bananas'] # result d = {'apples': 5, 'pears': 8} e = {'apples': 5, 'pears': 8, 'bananas': 1} d = {'apples': 5, 'pears': 8} d = {'apples': 5} d = {'watermelons': 315, 'blueberries': 2, 'apples': 5} e = {'apples': 5, 'pears': 8, 'bananas': 1} e['bananas'] = 1 # e[0]=3 # would give: # AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..." 

Consulte el patrón de modificación de lvalor que se describe aquí: Python 2.7: syntax de limpieza para la modificación de lvalue . La observación clave es que str e int comportan como valores en Python (aunque en realidad son objetos inmutables bajo el capó). Mientras observa eso, también observe que nada es mágicamente especial en str o int . dict se puede usar de la misma manera, y puedo pensar en muchos casos en los que ValueDict tiene sentido.

porque, dict2 = dict1, dict2 contiene la referencia a dict1. Tanto dict1 como dict2 apuntan a la misma ubicación en la memoria. Este es solo un caso normal al trabajar con objetos mutables en Python. Cuando trabaje con objetos mutables en python, debe tener cuidado ya que es difícil de depurar. Como el siguiente ejemplo.

  my_users = { 'ids':[1,2], 'blocked_ids':[5,6,7] } ids = my_users.get('ids') ids.extend(my_users.get('blocked_ids')) #all_ids print ids#output:[1, 2, 5, 6, 7] print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]} 

La intención de este ejemplo es obtener todas las ID de usuario, incluidas las ID bloqueadas. Lo que obtuvimos de la variable ids, pero también actualizamos el valor de my_users involuntariamente. cuando extendió los identificadores con blocked_ids my_users se actualizó porque los identificadores se refieren a my_users .

Agradables explicaciones, quiero agregar la regla más simple a la que puede referirse cuando piense en las variables de Python que asigna igual a = . Si el tipo de datos es inmutable, no tiene que preocuparse por el comportamiento inesperado que encontró. Si el tipo de datos es mutable, desea asegurarse de hacer una copia de los mismos para evitar el comportamiento inesperado que haya encontrado.

Tipos de datos inmutables : cadena (una tupla de caracteres), tupla

Tipos de datos mutables : lista, matriz, diccionario

Puedes usar directamente:

 dict2 = eval(repr(dict1)) 

donde objeto dict2 es una copia independiente de dict1, por lo que puede modificar dict2 sin afectar a dict1.

Esto funciona para cualquier tipo de objeto.