1318 votes

Comment copier un dictionnaire et ne modifier que la copie ?

Quelqu'un peut-il m'expliquer cela ? Cela n'a aucun sens pour moi.

Je copie un dictionnaire dans un autre et je modifie le second, et les deux sont modifiés. Pourquoi cela se produit-il ?

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

11 votes

PythonTutor est idéal pour visualiser les références Python. Voici ce code à la dernière étape . Vous pouvez voir dict1 y dict2 pointent vers le même dictateur.

3 votes

Juste au cas où PythonTutor tomberait en panne, voici une capture d'écran des structures de données à la fin.

1291voto

Mike Graham Points 22480

Python jamais copie implicitement les objets. Lorsque vous définissez dict2 = dict1 vous faites en sorte qu'ils se réfèrent au même objet dict, de sorte que lorsque vous le mutez, toutes les références à cet objet continuent de se référer à 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)

o

dict2 = dict1.copy()

50 votes

Il serait peut-être préférable de dire "dict2 et dict1 pointent vers les même dictionnaire", vous ne changez pas dict1 ou dict2 mais ce vers quoi ils pointent.

1 votes

@GrayWizardx, je ne suis pas sûr de ce que vous suggérez. Vous pensez que je devrais utiliser le terme "dictionnaire" au lieu de "objet dict" ?

5 votes

Non, je disais que c'est un truc de pointeur, mais ensuite j'ai essayé de le réécrire sans avoir l'air d'un vieux programmeur C++ qui dit aux gens que les nouveaux langages sont nuls. C'est un problème commun pour les gens qui apprennent le C++ aussi. Votre réponse est parfaite telle quelle, et je l'ai déjà +1ée.

1048voto

Imran Points 20117

Lorsque vous attribuez dict2 = dict1 vous ne faites pas une copie de dict1 il en résulte que dict2 étant juste un autre nom pour dict1 .

Pour copier les types mutables comme les dictionnaires, utilisez copy / deepcopy de la copy module.

import copy

dict2 = copy.deepcopy(dict1)

138 votes

Pour tous les dictionnaires avec lesquels je travaille, deepcopy est ce dont j'ai besoin... Je viens de perdre plusieurs heures à cause d'un bug qui venait du fait que je n'obtenais pas une copie complète d'un dictionnaire imbriqué et que mes modifications des entrées imbriquées affectaient l'original.

15 votes

Même chose ici. deepcopy() fait l'affaire. J'étais en train d'embrouiller mes dicts imbriqués dans un cache rotatif en ajoutant un timestamp à une 'copie' de l'événement original. Merci !

17 votes

Cette réponse devrait en fait être marquée comme étant la bonne ; cette réponse est générale et fonctionne également pour un dictionnaire de dictionnaires.

270voto

appleater Points 51

Alors que dict.copy() y dict(dict1) génère une copie, ils sont seulement peu profond copies. Si vous voulez un profond copie, copy.deepcopy(dict1) est nécessaire. Un exemple :

>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> copy1 = x.copy()
>>> copy2 = dict(x)
>>> import copy
>>> copy3 = copy.deepcopy(x)
>>> source['a'] = 10  # a change to first-level properties won't affect copies
>>> source
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy3
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> source['b']['m'] = 40  # a change to deep properties WILL affect shallow copies 'b.m' property
>>> source
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy3  # Deep copy's 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}

En ce qui concerne les copies superficielles par rapport aux copies profondes, du Python copy documentation sur les modules :

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

  • Une copie superficielle construit un nouvel objet composé, puis (dans la mesure du possible) insère dans celui-ci des références aux objets présents dans l'original.
  • Une copie profonde construit un nouvel objet composé puis, de manière récursive, y insère des copies des objets présents dans l'original.

3 votes

Cela devrait être la bonne réponse car elle ne boucle pas explicitement sur le dict et peut être utilisée pour d'autres structures primaires.

36 votes

Juste pour clarifier : w=copy.deepcopy(x) est la ligne clé.

0 votes

Quelle est la différence entre dict2 = dict1 y dict2 = copy.deepcopy(dict1) ?

98voto

Vkreddy Points 904

In Depth et un moyen facile de s'en souvenir :

Chaque fois que vous faites dict2 = dict1, dict2 fait référence à dict1. Les deux dict1 et dict2 pointent vers le même emplacement dans la mémoire. Il s'agit d'un cas normal lorsque vous travaillez avec des objets mutables en Python. Lorsque vous travaillez avec des objets mutables en Python, vous devez être prudent car il est difficile de déboguer.

Au lieu d'utiliser dict2 = dict1, vous devriez utiliser copie (copie superficielle) et copie profonde de la méthode python copie pour séparer le dict2 du dict1.

Le bon chemin est :

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?"
>>> dict2
{'key1': 'value1', 'key2': 'WHY?'}
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>> id(dict1)
140641178056312
>>> id(dict2)
140641176198960
>>> 

Comme vous pouvez le constater, le id de dict1 et dict2 sont différentes, ce qui signifie que les deux pointent/référencent des emplacements différents dans la mémoire.

Cette solution fonctionne pour les dictionnaires avec des valeurs immuables, ce n'est pas la bonne solution pour ceux avec des valeurs mutables.

Eg :

>>> import copy
>>> dict1 = {"key1" : "value1", "key2": {"mutable": True}}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': False}}
>>> id(dict1)
140641197660704
>>> id(dict2)
140641196407832
>>> id(dict1["key2"])
140641176198960
>>> id(dict2["key2"])
140641176198960

Vous pouvez voir que même si nous avons appliqué la copie pour dict1, la valeur de mutable est changée en false à la fois sur dict2 et dict1, même si nous ne la changeons que sur dict2. C'est parce que nous avons changé la valeur d'une partie de dict mutable de dict1. Lorsque nous appliquons une copie sur dict, elle ne fera qu'une copie superficielle, ce qui signifie qu'elle copie toutes les valeurs immuables dans un nouveau dict et ne copie pas les valeurs mutables, mais elle les référencera.

La solution ultime est de faire une copie profonde de dict1 pour créer complètement un nouveau dict avec toutes les valeurs copiées, y compris les valeurs mutables.

>>>import copy
>>> dict1 = {"key1" : "value1", "key2": {"mutable": True}}
>>> dict2 = copy.deepcopy(dict1)
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> id(dict1)
140641196228824
>>> id(dict2)
140641197662072
>>> id(dict1["key2"])
140641178056312
>>> id(dict2["key2"])
140641197662000
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': True}}

Comme vous pouvez le voir, les id sont différents, ce qui signifie que dict2 est un nouveau dict avec toutes les valeurs de dict1.

La copie profonde doit être utilisée si vous souhaitez modifier l'une des valeurs mutables sans affecter le dict original. Sinon, vous pouvez utiliser la copie superficielle. Deepcopy est lent car il travaille de manière récursive pour copier toutes les valeurs imbriquées dans le dict original et prend également de la mémoire supplémentaire.

77voto

PabTorre Points 1597

Sur python 3.5+, il existe un moyen plus simple d'obtenir une copie superficielle en utilisant l'opérateur de déballage **. 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éballe le dictionnaire dans un nouveau dictionnaire qui est ensuite affecté à dict2.

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

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

Si une copie profonde est nécessaire, alors copie.deepcopy() est toujours la voie à suivre.

3 votes

Cela ressemble terriblement aux pointeurs en C++. C'est bien pour accomplir la tâche, mais en termes de lisibilité, j'ai tendance à ne pas aimer ce type d'opérateurs.

1 votes

Il a un aspect un peu c'ish... mais lors de la fusion de plusieurs dictionnaires, la syntaxe semble assez fluide.

3 votes

Soyez prudent avec cela, il n'effectue qu'une copie superficielle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X