Tiefe Kopie eines Diktats in Python

340

Ich möchte eine tiefe Kopie von a dictin Python machen. Leider existiert die .deepcopy()Methode für die nicht dict. Wie mache ich das?

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

Die letzte Zeile sollte sein 3.

Ich möchte, dass Änderungen in my_dictden Schnappschuss nicht beeinflussen my_copy.

Wie mache ich das? Die Lösung sollte mit Python 3.x kompatibel sein.

Olivier Grégoire
quelle
3
Ich weiß nicht, ob es sich um ein Duplikat handelt, aber dies: stackoverflow.com/questions/838642/python-dictionary-deepcopy ist furchtbar nahe.
Charleslparker

Antworten:

472

Wie wäre es mit:

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

Python 2 oder 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
>>>
Lasse V. Karlsen
quelle
16
In der Tat funktioniert das für das vereinfachte Beispiel, das ich gegeben habe. Meine Schlüssel sind keine Zahlen, sondern Objekte. Wenn ich die Dokumentation zum Kopiermodul lese, muss ich eine __copy __ () / __ deepcopy __ () -Methode für die Schlüssel deklarieren. Vielen Dank, dass Sie mich dorthin geführt haben!
Olivier Grégoire
3
Gibt es einen Unterschied zwischen Python 3.2- und 2.7-Codes? Sie scheinen mir identisch zu sein. Wenn ja, wäre besser ein einzelner Codeblock und eine Anweisung "Funktioniert für Python 3 und 2"
MestreLion
30
Erwähnenswert copy.deepcopyist auch, dass es nicht threadsicher ist. Das habe ich auf die harte Tour gelernt. Auf der anderen Seite, je nach Anwendungsfall, json.loads(json.dumps(d)) ist Thread - sicher und funktioniert gut.
Rob
1
@rob du solltest diesen Kommentar als Antwort posten. Es ist eine praktikable Alternative. Die Fadensicherheitsnuance ist eine wichtige Unterscheidung.
BuvinJ
3
@BuvinJ Das Problem ist, dass json.loadsdas Problem nicht für alle Anwendungsfälle gelöst wird, in denen Python- dictAttribute nicht JSON-serialisierbar sind. Es mag denen helfen, die sich nur mit einfachen Datenstrukturen befassen, beispielsweise mit einer API, aber ich denke nicht, dass es ausreicht, um die Frage des OP vollständig zu beantworten.
Rob
35

dict.copy () ist eine flache Kopierfunktion für die integrierte Wörterbuch-
ID , die Ihnen die Adresse der Variablen gibt

Zuerst müssen Sie verstehen, warum dieses spezielle Problem auftritt.

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

Die Adresse der Liste in beiden Diktaten für Schlüssel 'a' zeigt auf dieselbe Stelle.
Wenn Sie den Wert der Liste in my_dict ändern, ändert sich daher auch die Liste in my_copy.


Lösung für die in der Frage erwähnte Datenstruktur:

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

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

Oder Sie können Deepcopy wie oben erwähnt verwenden.

theBuzzyCoder
quelle
4
Ihre Lösung funktioniert nicht für verschachtelte Wörterbücher. Aus diesem Grund ist eine Deepcopy vorzuziehen.
Charles Plager
2
@ CharlesPlager Einverstanden! Sie sollten jedoch auch beachten, dass das Aufteilen von Listen nicht mit Diktaten funktioniert value[:]. Die Lösung war eher für die in der Frage erwähnte Datenstruktur als für eine universelle Lösung.
TheBuzzyCoder
16

Python 3.x.

von copy import deepcopy

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

Ohne Deepcopy kann ich das Hostnamen-Wörterbuch nicht aus meinem Domain-Wörterbuch entfernen.

Ohne Deepcopy erhalte ich folgenden Fehler:

"RuntimeError: dictionary changed size during iteration"

... wenn ich versuche, das gewünschte Element aus meinem Wörterbuch in einem anderen Wörterbuch zu entfernen.

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

Domain ist ein Wörterbuchobjekt

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

Beispielausgabe: [orginal] domain = {'localdomain': {'localhost': {'all': '4000'}}}

[neue] Domains = {'localdomain': {}}}

Was hier also vor sich geht, ist, dass ich über eine Kopie eines Wörterbuchs iteriere, anstatt über das Wörterbuch selbst zu iterieren. Mit dieser Methode können Sie Elemente nach Bedarf entfernen.

xpros
quelle
-2

Ich mag und habe viel von Lasse V. Karlsen gelernt. Ich habe es in das folgende Beispiel geändert, das den Unterschied zwischen flachen Wörterbuchkopien und tiefen Kopien ziemlich gut hervorhebt:

    import copy

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

Nun, wenn Sie sich ändern

    my_dict['a'][2] = 7

und TU

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

du erhältst

    >> my_copy a[2]:  7 ,whereas my_deepcopy a[2]:  3
Rafael Monteiro
quelle
Warum unterscheidet sich diese Antwort Ihrer Meinung nach von der Antwort von Lasse V. Karlsen ? Was fügt es hinzu, dass die andere Antwort nicht sagt?
Olivier Grégoire
Hallo Olivier! Ich versuche nicht, die Antwort von Lasse V. Karlsen zu würdigen - er hat im Wesentlichen das Problem gelöst, das ich hatte, und ich bin ihm gegenüber schuldig. Mein Kommentar ist nicht anders, er ist nur komplementär. Aus dem einfachen Grund, dass es "Kopie" mit "Deepcopy" kontrastiert. Dies war die Ursache meines Problems, denn ich habe mich geirrt, wenn ich sie auf gleichwertige Weise verwendet habe. Prost.
Rafael Monteiro
-8

Eine einfachere (meiner Ansicht nach) Lösung besteht darin, ein neues Wörterbuch zu erstellen und es mit dem Inhalt des alten zu aktualisieren:

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

Das Problem bei diesem Ansatz ist, dass er möglicherweise nicht tief genug ist. dh ist nicht rekursiv tief. gut genug für einfache Objekte, aber nicht für verschachtelte Wörterbücher. Hier ist ein Beispiel, bei dem es möglicherweise nicht tief genug ist:

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

Mit Deepcopy () kann ich das halbflache Verhalten beseitigen, aber ich denke, man muss entscheiden, welcher Ansatz für Ihre Anwendung geeignet ist. In den meisten Fällen ist es Ihnen vielleicht egal, aber Sie sollten sich der möglichen Fallstricke bewusst sein ... letztes Beispiel:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

my_copy2
Out[46]: {'b': {'b': 'z'}, 'c': {'c': 3}}
Eric Hoffman
quelle
12
Dies macht eine flache Kopie des Diktats, was der Fragesteller nicht verlangt hat. Die darin enthaltenen Objekte werden nicht selbst kopiert. Und eine einfachere Möglichkeit zum flachen Kopieren ist my_dict.copy()!
Blckknght