辞書をコピーして編集のみを行う方法


856

誰かがこれを私に説明してくれませんか?これは私には意味がありません。

辞書を別の辞書にコピーして2番目の辞書を編集すると、両方が変更されます。なんでこんなことが起こっているの?

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

4
PythonTutorは、Pythonリファレンスの視覚化に最適です。これが最後のステップでのこのコードです。あなたは同じ辞書を見てdict1dict2指し示すことができます。
wjandrea

回答:


883

Python 暗黙的にオブジェクトをコピーすることはありません。を設定するとdict2 = dict1、それらはまったく同じdictオブジェクトを参照するようになります。そのため、これを変更すると、すべての参照は現在の状態のオブジェクトを参照し続けます。

dictをコピーする場合(これはまれです)、明示的に次のように行う必要があります。

dict2 = dict(dict1)

または

dict2 = dict1.copy()

26
「dict2とdict1は同じディクショナリを指している」と言った方がいいかもしれません。dict1やdict2を変更するのではなく、それらが指すものを変更することになります。
GrayWizardx 2010年

276
また、dict.copy()は浅いことに注意してください。ネストされたリストなどがある場合、変更は両方に適用されます。ちゃんと覚えたら。ディープコピーはそれを回避します。
2010年

16
Pythonがオブジェクトを暗黙的にコピーすることは決してありません。int、float、boolなどのプリミティブデータ型もオブジェクトとして扱われます(これdir(1)を確認するにはa を実行します)が、暗黙的にコピーされます。
daniel kullmann

17
@danielkullmann、私はあなたが扱ってきた他の言語がどのように機能しているかに基づいて、Pythonについて誤解があるかもしれないと思います。Pythonでは、a)「プリミティブデータ型」の概念はありません。intfloat、およびboolインスタンスは、実際のPythonはオブジェクト、そしてあなたがそれらを渡すときb)はこれらの型のオブジェクトが暗黙のうちにいなくてもCPythonの中に、実装の詳細などわからないためのセマンティックPythonのレベルでは、コピーされません。です
マイクグラハム

39
「ディープコピーは有害と見なされます」のような根拠のないレトリックは役に立ちません。他のすべてが等しい場合、複雑なデータ構造を浅くコピーすると、同じ構造を深くコピーするよりも、予期しないエッジケースの問題が発生する可能性が大幅に高くなります。変更によって元のオブジェクトが変更されるコピーは、コピーではありません。それはバグです。エルゴ、ほとんどのユースケースでは、またはではなく絶対に呼び出す必要があります。イムラン簡潔な答えは、この答えとは異なり、正気の右側にあります。copy.deepcopy()dict()dict.copy()
セシルカレー

647

を割り当てるdict2 = dict1と、のコピーは作成dict1dict2れず、の別名になりdict1ます。

辞書のような変更可能なタイプをコピーするには、モジュールのcopy/ deepcopyを使用しcopyます。

import copy

dict2 = copy.deepcopy(dict1)

80
私がこれまでに使用したどのディクショナリでも、deepcopyが必要です...ネストされたディクショナリの完全なコピーを取得しておらず、ネストされたエントリへの変更が元のディクショナリに影響していたため、バグのために数時間失われました。
flutefreak7 14

7
こっちも一緒。deepcopy()がトリックを行います。元のイベントの「コピー」にタイムスタンプを追加することで、回転キャッシュ内のネストされたディクテーションをめちゃくちゃにしていました。ありがとうございました!
fxstein 2015

8
これは実際には正解としてマークする必要があります。この答えは一般的で、辞書の辞書でも機能します。
orezvani

30
これは受け入れられる答えになるはずです。現在受け入れられている回答のコメントセクションに埋め込まれた根拠のない「ディープコピーは有害と見なされる」レトリックは、ネストされた辞書(ここに記載されているものなど)をコピーするときに同期の問題を露骨に招き、そのように挑戦する必要があります。
Cecil Curry

ディープコピーは、複雑な辞書構造の場合に使用する方法です。dict1.copy()は、オブジェクトとしてではなく、参照としてキーの値を単にコピーします。
Rohith N

182

コピーdict.copy()dict(dict1)生成する間、それらは浅いコピーのみです。詳細コピーcopy.deepcopy(dict1)が必要な場合は、が必要です。例:

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

浅いコピーと深いコピーについて、Python copyモジュールのドキュメントから

シャローコピーとディープコピーの違いは、複合オブジェクト(リストやクラスインスタンスなどの他のオブジェクトを含むオブジェクト)にのみ関係します。

  • 浅いコピーは、新しい複合オブジェクトを構築し、(可能な範囲で)元のオブジェクトにあるオブジェクトへの参照を挿入します。
  • ディープコピーは、新しい複合オブジェクトを作成し、再帰的に、元のオブジェクトにあるオブジェクトのコピーをそれに挿入します。

2
これは、dictを明示的にループするのではなく、他の主要な構造に使用できるので、正しい答えになるはずです。
Nikkolasg、2015年

27
ただ明確にするために:w=copy.deepcopy(x)重要な行です。
アルコール依存症

違いは何であるdict2 = dict1とはdict2 = copy.deepcopy(dict1)
TheTank

1
@TheTank、y = xは、2つの名前(参照)が同じオブジェクトを参照するようにします。つまり、「y is x」はTrueです。xを介してオブジェクトに加えられた変更は、yを介した同じ変更と同等です。ただし、u、v、wは、インスタンス化中にxからコピーされた値を持つ新しい異なるオブジェクトへの参照です。u、v(浅いコピー)とw(deepcopy)の違い​​については、docs.python.org
copy.html

63

Python 3.5以降では、** unpackaging演算子を使用して浅いコピーを実現する簡単な方法があります。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?!'}

**辞書を新しい辞書にパッケージ解除し、新しい辞書をdict2に割り当てます。

また、各辞書に異なるIDがあることも確認できます。

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

ディープコピーが必要な場合は、copy.deepcopy()を使用してください。


3
これは、C ++のポインタと非常に似ています。タスクを達成するためにいいですが、読みやすさに関しては、このタイプの演算子は嫌いです。
Ernesto

1
それは一種のc'ishの外観をしています...しかし、複数の辞書をマージするとき、構文はかなり滑らかに見えます。
PabTorre 2017

2
それは注意してください、それは浅いコピーのみを実行します。
セバスチャンドレスラー

あなたは正しい@SebastianDressler、私は調整をマクデにします。thnx。
PabTorre

2
あなたには、いくつかのspiciesでコピーを作成したい場合に便利:dict2 = {**dict1, 'key3':'value3'}
evg656e

48

最良とする最も簡単な方法コピー作成辞書を両方でのPython 2.7と3があります...

シンプルな(単一レベルの)辞書のコピーを作成するには:

1.既存のdictを指す参照を生成する代わりに、dict()メソッドを使用します

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. Python辞書の組み込みupdate()メソッドを使用します。

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

ネストされた辞書または複雑な辞書のコピーを作成するには:

組み込みのコピーモジュールを使用します。これは、一般的なシャローおよびディープコピー操作を提供します。このモジュールはPython 2.7と3の両方に存在します。*

import copy

my_dict2 = copy.deepcopy(my_dict1)

6
dict()深いコピーではなく、浅いコピーを作成すると思います。つまり、ネストされているdict場合、外側dictはコピーになりますが、内側の辞書は元の内側の辞書への参照になります。
shmuels 2018

@shmuelsはい、これらの方法はどちらも、深いコピーではなく、浅いコピーを作成します。更新された答えを参照してください。
AKay Nirala 2018

37

辞書を理解した新しい辞書を作ることもできます。これにより、コピーのインポートが回避されます。

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

もちろんpython> = 2.7では次のことができます:

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

ただし、下位互換性については、topメソッドの方が優れています。


4
これは、正確にコピーする方法と内容をより詳細に制御する場合に特に便利です。+1
接近

14
このメソッドはディープコピーを実行しないことに注意してください。コピーするキーを制御する必要のないシャローコピーがd2 = dict.copy(d1)必要な場合は、インポートも必要ありません。
JarekPiórkowski、2015

1
JarekPiórkowski@:またはあなたがメソッドのようなメソッドを呼び出すことができますd2 = d1.copy()
Azat Ibrakov

最初の例では理解が必要ないことに注意してください。dict.itemsすでに反復可能なキーと値のペアを返します。だから、あなたは単に使うことができますdict(mydict.items())(あなたは単に使うこともできますdict(mydict))。エントリをフィルタリングする場合は、理解があると便利です。
ポールルーニー

22

提供されている他のソリューションに加えて、を使用**して辞書を空の辞書に統合できます。たとえば、

shallow_copy_of_other_dict = {**other_dict}

これで、の「浅い」コピーが作成されますother_dict

あなたの例に適用:

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

ポインター:浅いコピーと深いコピーの違い


1
これにより、深いコピーではなく、浅いコピーになります。
sytech

1
私はこれを試していましたが、問題がありました。これはpython 3.5以降でのみ機能します。python.org/dev/peps/pep-0448
ThatGuyRob 2018

19

Pythonの代入ステートメントはオブジェクトをコピーせず、ターゲットとオブジェクト間のバインディングを作成します。

そのため、参照するオブジェクトとのdict2 = dict1間の別のバインディングが発生dict2しますdict1

辞書をコピーしたい場合は、を使用できますcopy module。コピーモジュールには2つのインターフェイスがあります。

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

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

シャローコピーとディープコピーの違いは、複合オブジェクト(リストやクラスインスタンスなどの他のオブジェクトを含むオブジェクト)にのみ関係します。

浅いコピーオリジナルで見つかったオブジェクトにその中に(可能な限り)を挿入参照を新しい複合オブジェクトを作成し、。

ディープコピーは、新しい複合オブジェクトを作成し、その後、再帰的に、オブジェクトのそれへの挿入のコピーはオリジナルで見つかりました。

たとえば、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')

結果は次のとおりです。

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

10

dict追加のキーワード引数を指定してコンストラクターを呼び出すことにより、新しく作成されたコピーを一度にコピーして編集できます。

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

9

私もCのバックグラウンドから来ていたので、これも私を混乱させました。

Cでは、変数は型が定義されたメモリ内の場所です。変数に割り当てると、データが変数のメモリ位置にコピーされます。

しかし、Pythonでは、変数はオブジェクトへのポインタのように機能します。したがって、ある変数を別の変数に割り当ててもコピーは作成されず、変数名が同じオブジェクトを指すようになります。


5
Python変数はC ++参照のように機能します
Ruggero Turra

7
Pythonのすべてがオブジェクトだからです! diveintopython.net/getting_to_know_python/… (はい、この応答は何年も遅れていますが、おそらく誰かに
役立つでしょ

1
Python言語のセマンティクスでは「変数」はないと言っています。それらは「名前付き参照」と呼ばれます。オブジェクトへの参照がコード内の構文文字列であることを意味します。オブジェクトには、多くの名前付き参照を含めることができます。intやfloatのような不変オブジェクトとstrインスタンスは、プロセスごとにそのインスタンスを1つだけ持っています。これを実行すると、メモリ内のintが1でも、同じメモリアドレスで2または他の値に変更されませんmyvalue = 1 myvalue = 2
DevPlayer

7

Pythonのすべての変数(dict1またはstrまたは__builtins__は、マシン内の隠されたプラトニックな「オブジェクト」へのポインタです。

を設定した場合は、と同じオブジェクト(またはメモリの場所など)をdict1 = dict2ポイントdict1するだけですdict2。現在、によって参照されるオブジェクトdict1は、によって参照されるオブジェクトと同じdict2です。

あなたはチェックすることができます:でdict1 is dict2なければなりませんTrue。また、id(dict1)と同じである必要がありid(dict2)ます。

あなたが欲しいdict1 = copy(dict2)、またはdict1 = deepcopy(dict2)

違いcopydeepcopy?(リストでポイントしましたか?)の要素もコピーであるdeepcopyことを確認dict2します。

私はあまり使用しませんdeepcopy-(私の意見では)それを必要とするコードを書くことは通常悪い習慣です。


ネストされたディクショナリをコピーしてネストされたエントリの変更を開始すると、影響が元のコピーではなくコピーでのみ発生するように、常にディープコピーを使用する必要があることに気付きました。
flutefreak7 14

6

dict1基になる辞書オブジェクトを参照するシンボルです。割り当てdict1dict2過ぎないことは、同一の参照を割り当てます。dict2記号を使用してキーの値を変更すると、基になるオブジェクトが変更され、これも影響しますdict1ます。これは紛らわしいです。

不変の値については、参照よりも推論する方がはるかに簡単なので、可能な限りコピーを作成します。

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

これは構文的には次と同じです。

one_year_later = dict(person, age=26)

5

dict2 = dict1辞書をコピーしません。プログラマdict2に、同じ辞書を参照するための2番目の方法()を提供するだけです。


5
>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1

Dictオブジェクトをコピーするには多くの方法がありますが、私は単に使用します

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

12
dict_2 = dict_1.copy()はるかに効率的で論理的です。
ジャン=フランソワ・ファーブル

2
dict1内にdictがある場合、dict_1.copy()を使用して、dict_2の内部dictで行った変更がdict_1の内部dictにも適用されることに注意してください。この場合、代わりにcopy.deepcopy(dict_1)を使用する必要があります。
クイズ

1

他の人が説明したように、ビルトインdictはあなたが望むことをしません。しかし、Python2(およびおそらく3でも)ではValueDict、コピーするクラスを簡単に作成できるため、元のクラスが=変更されないことを確認できます。

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

ここで説明されている左辺値変更パターンを参照してください:Python 2.7-右辺値変更のクリーンな構文。重要な観察はそれでstrありint、Pythonの値として動作します(実際には内部で不変のオブジェクトですが)。あなたがそれを観察している間、strまたはについて魔法の特別なものは何もないことにも注意してくださいintdictほとんど同じ方法で使用できValueDict、意味のある多くのケースを考えることができます。


0

次のコードは、deepcopyの3倍以上の高速でjson構文に従うdictにあります

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

変数に割り当てずにクラスのディクショナリプロパティをディープコピーしようとすると、奇妙な動作に遭遇しました

new = copy.deepcopy(my_class.a) 動作しません、すなわち変更 new変更しますmy_class.a

しかし、それを行うold = my_class.aと、new = copy.deepcopy(old)それは完全に機能しますnewには影響しませんmy_class.a

これがなぜ起こるかはわかりませんが、それが数時間の節約に役立つことを願っています!:)


では、どのようにしてディープコピーを作成しますmy_class.aか?
アンソニー

最善の方法ではありません。良い反応は怒鳴ります。
David Beauchemin

-1

dict2 = dict1、dict2はdict1への参照を保持するためです。dict1とdict2はどちらもメモリ内の同じ場所を指します。これは、Pythonで可変オブジェクトを操作する場合の通常のケースです。Pythonで可変オブジェクトを操作する場合、デバッグが難しいので注意する必要があります。次の例のように。

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

この例の意図は、ブロックされたIDを含むすべてのユーザーIDを取得することです。ids変数から取得したが、my_usersの値も意図せずに更新した。あなたが拡張するとき、IDSblocked_idsのためmy_usersが更新されましたIDはを参照してくださいmy_users


-1

forループを使用したコピー:

orig = {"X2": 674.5, "X3": 245.0}

copy = {}
for key in orig:
    copy[key] = orig[key]

print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 674.5, 'X3': 245.0}
copy["X2"] = 808
print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 808, 'X3': 245.0}

1
これは単純な辞書に対してのみ機能します。deepcopyこの目的のために特別に作成されたを使用しないのはなぜですか?
アンソニー

最善の方法ではありません。良い反応は怒鳴ります。
David Beauchemin

-6

直接使用できます:

dict2 = eval(repr(dict1))

オブジェクトdict2はdict1の独立したコピーなので、dict1に影響を与えることなくdict2を変更できます。

これはあらゆる種類のオブジェクトで機能します。


4
この答えは正しくないため、使用しないでください。たとえば、ユーザー定義のクラスは、__repr__evalによって再構築される適切なクラスを持たない場合もあり、オブジェクトのクラスが呼び出される現在のスコープ内にある場合もありません。組み込み型を使用している場合でも、同じオブジェクトが複数のキーの下に格納されている場合、dict22つの別々のオブジェクトがある場合、これは失敗します。dict1自身を含む自己参照辞書は、代わりにを含みますEllipsis。使用する方がよいでしょうdict1.copy()
Eldritchチーズ

オブジェクト(または「値」)は、文字列による忠実な表現が常にあるとは限りません。いかなる場合でも、通常の人間が読める方法ではありません。
Alexey
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.