Pythonでのdictのディープコピー


340

dictPythonでのの深いコピーを作成したいと思います。残念ながら、この.deepcopy()メソッドはには存在しませんdict。それ、どうやったら出来るの?

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

最終行はになります3

変更がmy_dictスナップショットに影響を与えないようにしてくださいmy_copy

それ、どうやったら出来るの?ソリューションはPython 3.xと互換性があるはずです。


3
重複しているかどうかはわかりませんが、これは次のとおりです。stackoverflow.com / questions / 838642 / python-dictionary-deepcopyは非常に近いです。
charleslparker 2013年

回答:


472

どうですか:

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

Python 2または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
確かに、これは私が示した単純化した例で機能します。私の鍵は数字ではなくオブジェクトです。コピーモジュールのドキュメントを読んだ場合、キーの__copy __()/ __ deepcopy __()メソッドを宣言する必要があります。そこで私を導いてくれてありがとう!
オリヴィエ・グレゴワール

3
Python 3.2と2.7のコードに違いはありますか?彼らは私と同じように見えます。もしそうなら、コードの単一のブロックとステートメント「Python 3と2の両方で機能する」の方が良いでしょう
MestreLion

30
また、copy.deepcopyスレッドセーフではないことにも言及する価値があります。これを難しい方法で学びました。一方、ユースケースによってjson.loads(json.dumps(d)) は、スレッドセーフであり、適切に機能します。
ロブ・

1
@robそのコメントを回答として投稿する必要があります。それは実行可能な代替手段です。スレッドセーフのニュアンスは重要な違いです。
BuvinJ 2017年

3
@BuvinJ問題はjson.loads、Python dict属性がJSONシリアライズ可能でないすべてのユースケースの問題を解決しないことです。たとえばAPIから、単純なデータ構造のみを扱う人には役立つかもしれませんが、OPの質問に完全に答えるのに十分な解決策ではないと思います。
2017年

36

dict.copy()は辞書
IDの浅いコピー関数で あり、変数のアドレスを提供する組み込み関数です

まず、「なぜこの特定の問題が発生しているのか」を理解する必要があります。

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

キー「a」の両方の辞書に存在するリストのアドレスは、同じ場所を指しています。
したがって、my_dictのリストの値を変更すると、my_copyのリストも変更されます。


質問に記載されているデータ構造の解決策:

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

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

または、上記のようにディープコピーを使用できます。


4
ソリューションはネストされた辞書では機能しません。そのため、ディープコピーが望ましいです。
Charles Plager

2
@CharlesPlager同意した!ただし、リストのスライスはdictでは機能しないことにも注意してくださいvalue[:]。解決策は、普遍的な解決策ではなく、質問で言及された特定のデータ構造に対するものでした。
theBuzzyCoder 2018

17

Python 3.x

コピーインポートからディープコピー

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

ディープコピーがないと、ドメインディクショナリ内からホスト名ディクショナリを削除できません。

ディープコピーがないと、次のエラーが発生します。

"RuntimeError: dictionary changed size during iteration"

...目的の要素を別の辞書内の辞書から削除しようとしたとき。

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

ドメインは辞書オブジェクトです

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

出力例:[orginal] domains = {'localdomain':{'localhost':{'all': '4000'}}}

[new] domains = {'localdomain':{}}}

したがって、ここで行われているのは、辞書自体を反復するのではなく、辞書のコピーを反復処理していることです。このメソッドを使用すると、必要に応じて要素を削除できます。


-2

Lasse V. Karlsenから多くを学び、多くを学びました。それを次の例に変更しました。これは、浅い辞書のコピーと深いコピーの違いをかなりよく示しています。

    import copy

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

変更した場合

    my_dict['a'][2] = 7

そして、やります

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

あなたは得る

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

この回答がLasse V. Karlsenの回答と異なるのはなぜですか。他の答えが言っていないことは何を追加しますか?
OlivierGrégoire

こんにちは、オリヴィエ!私はラッセV.カールセンの答えのメリットを引き出すつもりはありません彼は本質的に私が抱えていた問題を解決し、私は彼に借金をしています。私のコメントも同じで、補足的なものです。「コピー」と「ディープコピー」を対比するという単純な理由で。これが私の問題の原因でした。同等の方法でそれらを使用するときに私は誤っていたからです。乾杯。
ラファエルモンテイロ

-8

よりシンプルな(私の見解では)解決策は、新しい辞書を作成し、古い辞書の内容で更新することです。

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

このアプローチの問題は、「十分に深く」ない可能性があることです。つまり、再帰的に深くはありません。単純なオブジェクトには十分ですが、ネストされた辞書には十分ではありません。以下は、深さが足りない例です。

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

Deepcopy()を使用することで、準浅い動作を排除できますが、アプリケーションに適したアプローチを決定する必要があると思います。ほとんどの場合、あなたは気にしないかもしれませんが、起こり得る落とし穴に気をつけるべきです...最後の例:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

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

12
これは、質問者が求めていたものではない、dictの浅いコピーを作成します。含まれているオブジェクト自体はコピーされません。そして、浅いコピーのより簡単な方法はmy_dict.copy()
Blckknght 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.