web-dev-qa-db-fra.com

Comment copier un dictionnaire et ne modifier que la copie

Quelqu'un pourrait-il me l'expliquer? Cela n'a aucun sens pour moi.

Je copie un dictionnaire dans un autre et édite le second et les deux sont modifiés. Pourquoi cela arrive-t-il?

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

Python jamais copie implicitement les objets. Lorsque vous définissez dict2 = dict1, vous les faites se référer au même objet dict exact. Par conséquent, lorsque vous le modifiez, toutes les références à celui-ci continuent à faire référence à l'objet dans son état actuel.

Si vous voulez copier le dict (ce qui est rare), vous devez le faire explicitement avec

dict2 = dict(dict1)

ou

dict2 = dict1.copy()
742
Mike Graham

Lorsque vous attribuez dict2 = dict1, vous ne copiez pas dict1, dict2 n'est qu'un autre nom pour dict1.

Pour copier les types modifiables tels que les dictionnaires, utilisez copy/deepcopy du module copy .

import copy

dict2 = copy.deepcopy(dict1)
517
Imran
>>> 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}}
132
gpanda

Sur python 3.5+, il existe un moyen plus simple de réaliser une copie superficielle à l'aide de l'opérateur ** de dépaquetage. Défini par 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?!'}

** décompresse le dictionnaire dans un nouveau dictionnaire qui est ensuite attribué à dict2.

Nous pouvons également confirmer que chaque dictionnaire a un identifiant distinct.

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

Si une copie en profondeur est nécessaire, alors copy.deepcopy () est toujours le chemin à parcourir.

59
PabTorre

Les meilleurs et les plus faciles moyens de créer une copie d'un dict dans les deux Python 2.7 et = sont ...

Pour créer une copie du dictionnaire simple (à un niveau):

1. Utiliser la méthode dict (), au lieu de générer une référence qui pointe vers le dict existant.

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. Utilisation de la méthode intégrée pdate () du dictionnaire 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'}

Pour créer une copie du dictionnaire imbriqué ou complexe:

Utilisez le module copie intégré, qui fournit une copie générique des opérations de copie superficielle et profonde. Ce module est présent dans Python 2.7 et 3. *

import copy

my_dict2 = copy.deepcopy(my_dict1)
42
Emrit

Vous pouvez également créer un nouveau dictionnaire avec une compréhension du dictionnaire. Cela évite d'importer une copie.

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

Bien sûr, dans python> = 2.7, vous pouvez faire:

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

Mais pour les compatriotes, la méthode la plus efficace est préférable.

35

Les instructions d'assignation dans Python ne copient pas les objets, elles créent des liaisons entre une cible et un objet.

alors, dict2 = dict1, il en résulte une nouvelle liaison entre dict2 et l'objet auquel dict1 fait référence.

si vous voulez copier un dict, vous pouvez utiliser le copy module. Le module de copie a deux interfaces:

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

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

La différence entre la copie superficielle et profonde ne concerne que les objets composés (objets contenant d'autres objets, tels que des listes ou des instances de classe):

Une copie superficielle construit un nouvel objet composé et y insère (dans la mesure du possible) des références aux objets trouvés dans l'original.

Une copie profonde construit un nouvel objet composé et y insère ensuite, de manière récursive, des copies des objets trouvés dans l'original.

Par exemple, dans 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')

et le résultat est:

>>> 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']]
17
loosen

Outre les autres solutions fournies, vous pouvez utiliser _**_ pour intégrer le dictionnaire dans un dictionnaire vide, par exemple,

shallow_copy_of_other_dict = {**other_dict}.

Maintenant, vous aurez une copie "superficielle" de _other_dict_.

Appliqué à votre exemple:

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

Pointeur: Différence entre les copies peu profondes et profondes

16
d4rty

Vous pouvez copier et éditer la copie nouvellement construite en une fois en appelant le constructeur dict avec des arguments de mots clés supplémentaires:

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

Cela m’a confondu aussi, au début, parce que je venais d’un milieu C.

En C, une variable est un emplacement en mémoire avec un type défini. L'affectation à une variable copie les données dans l'emplacement mémoire de la variable.

Mais en Python, les variables agissent davantage comme des pointeurs sur des objets. Donc, assigner une variable à une autre ne crée pas de copie, cela fait simplement que le nom de la variable pointe vers le même objet.

9
Craig McQueen

Chaque variable de python (un objet comme dict1 ou str ou __builtins__ est un pointeur sur un "objet" platonique masqué dans la machine.

Si vous définissez dict1 = dict2, il vous suffit de pointer dict1 vers le même objet (ou emplacement mémoire, ou selon l'analogie de votre choix) en tant que dict2. Maintenant, l'objet référencé par dict1 est le même objet référencé par dict2.

Vous pouvez vérifier: dict1 is dict2 devrait être True. De plus, id(dict1) devrait être identique à id(dict2).

Vous voulez dict1 = copy(dict2) ou dict1 = deepcopy(dict2).

La différence entre copy et deepcopy? deepcopy s'assurera que les éléments de dict2 (l'avez-vous dirigée vers une liste?) Sont également des copies.

Je n'utilise pas beaucoup deepcopy - c'est généralement une mauvaise pratique d'écrire du code qui en a besoin (à mon avis).

7
wisty

dict2 = dict1 ne copie pas le dictionnaire. Cela donne simplement au programmeur un second moyen (dict2) de se référer au même dictionnaire.

6
user97370

dict1 est un symbole qui fait référence à un objet de dictionnaire sous-jacent. L'attribution de dict1 à dict2 affecte simplement la même référence. La modification de la valeur d'une clé à l'aide du symbole dict2 modifie l'objet sous-jacent, ce qui affecte également dict1. Ceci est déroutant.

Il est beaucoup plus facile de raisonner sur les valeurs immuables que sur les références, alors faites des copies autant que possible:

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

Ceci est syntaxiquement identique à:

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

Il y a plusieurs façons de copier un objet Dict, j'utilise simplement

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

Comme d'autres l'ont expliqué, la dict intégrée ne fait pas ce que vous voulez. Mais en Python2 (et probablement 3 aussi), vous pouvez facilement créer une classe ValueDict qui copie avec = afin que vous puissiez être sûr que l'original ne changera pas.

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] = ..."

Veuillez vous reporter au modèle de modification de lvalue décrit ici: Python 2.7 - syntaxe propre pour la modification de lvalue . L'observation clé est que str et int se comportent comme des valeurs dans Python (même s'il s'agit d'objets immuables sous le capot). Pendant que vous observez cela, veuillez également noter que rien n’a de magie particulière sur str ou int. dict peut être utilisé à peu près de la même manière, et je peux penser à de nombreux cas où ValueDict a un sens.

1
personal_cloud

De bonnes explications, je veux ajouter la règle la plus simple à laquelle vous pouvez vous référer lorsque vous pensez à Python variables que vous affectez égales à =. Si le type de données est immuable, vous n'avez pas à vous soucier du comportement inattendu que vous avez rencontré. Si le type de données est modifiable, vous voulez vous assurer de le copier pour éviter le comportement inattendu que vous avez rencontré.

types de données immuables: chaîne (un tuple de caractères), tuple

types de données modifiables: liste, tableau, dictionnaire

1
Nikolay Shindarov

Étant donné que python fonctionne avec une référence, si vous avez dict2 = dict1, vous transmettez une référence à dict2, ce qui était identique à dict1. Ainsi, lorsque vous apportez une modification à dict1 ou dict2, vous modifiez une référence et les modifications des deux dictic. Désolé si je confonds quelque chose sur l'anglais.

1
Rodrigo Moraes

Juste pour les enregistrements. J'utilise le code suivant, qui est sur dicts et qui suit la syntaxe json plus de 3 fois plus vite que deepcopy

def CopyDict(dSrc):
    try:
        return json.loads(json.dumps(dSrc))
    except Exception as e:
        Logger.warning("Can't copy dict the preferred way:"+str(dSrc))
        return deepcopy(dSrc)
0
Carsten Thielepape

parce que, dict2 = dict1, dict2 contient la référence à dict1. Dict1 et dict2 pointent au même endroit dans la mémoire. Ceci est juste un cas normal lorsque vous travaillez avec des objets mutables en python. Lorsque vous travaillez avec des objets mutables dans python, vous devez faire attention car il est difficile de déboguer. Tels que l'exemple suivant.

 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]}

Cet exemple vise à obtenir tous les ID utilisateur, y compris les ID bloqués. Cela provient de la variable ids mais nous avons également mis à jour la valeur de my_users involontairement. lorsque vous avez étendu les ids avec block_ids mes_utilisateurs ont été mis à jour car ids se réfèrent à mes_utilisateurs .

0
Vkreddy Komatireddy