web-dev-qa-db-ja.com

辞書をコピーしてそのコピーだけを編集する方法

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

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

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

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

あなたが辞書をコピーしたい場合(まれですが)、あなたは明示的にそうする必要があります。

dict2 = dict(dict1)

または

dict2 = dict1.copy()
697
Mike Graham

dict2 = dict1を割り当てても、dict1のコピーは作成されません。その結果、dict2dict1の別の名前になります。

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

import copy

dict2 = copy.deepcopy(dict1)
470
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}}
124
gpanda

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() を使用してください。

57
PabTorre

辞書を使って新しい辞書を作ることもできます。これはコピーをインポートすることを避けます。

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

もちろん、Python> = 2.7では、次のことができます。

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

しかし、後方互換性のために、一番上の方法が良いです。

34

のコピーdictを両方ともPython 2.7と3は...)にする最も簡単で簡単な方法.

単純な(単一レベルの)辞書のコピーを作成するには、次のようにします。

既存の辞書を指す参照を生成する代わりに、1.dict()method)を使用します。

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

入れ子になった辞書または複雑な辞書のコピーを作成するには、次のようにします。

組み込みのcopyモジュールを使用してください。これは一般的なシャローコピーとディープコピーを提供します。このモジュールはPython 2.7と3の両方に存在します。

import copy

my_dict2 = copy.deepcopy(my_dict1)
30
Emrit

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

そのため、dict2 = dict1は、dict2dict1が参照するオブジェクトとの間の別のバインディングになります。

辞書をコピーしたい場合は、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']]
16
loosen

提供されている他の解決策に加えて、辞書を空の辞書に統合するために**を使うことができます。

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

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

14
d4rty

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

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

私はCの経歴から来ていたので、これも私を混乱させました。

Cでは、変数は定義された型を持つメモリ内の場所です。変数に代入すると、データが変数のメモリ位置にコピーされます。

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

9
Craig McQueen

Pythonのすべての変数(dict1str__builtins__のようなものは、マシン内の隠されたプラトンの「オブジェクト」へのポインタです。

dict1 = dict2を設定した場合は、dict1dict2と同じオブジェクト(またはメモリーの場所、またはそれに類するもの)を指すようにするだけです。現在、dict1によって参照されるオブジェクトは、dict2によって参照されるオブジェクトと同じです。

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

dict1 = copy(dict2)、またはdict1 = deepcopy(dict2)が必要です。

copydeepcopyの違いは? deepcopyは、dict2の要素(リストを指していますか?)もコピーであることを確認します。

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

7
wisty

dict2 = dict1は辞書をコピーしません。それは単にあなたに同じ辞書を参照するための第二の方法(dict2)をあなたに与えます。

6
user97370

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

Dictオブジェクトをコピーする方法はたくさんあります。

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

他の人が説明したように、組み込みのdictはあなたが望むことをしません。しかし、Python 2(そしておそらく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 - 左辺値修正のためのきれいな構文 。重要なのは、strintはPythonでは値として振る舞うということです(たとえそれらが実際には不変のオブジェクトであっても)。それを観察している間、strintに関して魔法のように特別なことは何もないことにも注意してください。 dictはほとんど同じように使用できます、そして私はValueDictが意味をなす多くの場合を考えることができます。

1
personal_cloud

わかりやすい説明ですが、=と等価に割り当てるPython変数について考えるときに参照できる最も簡単な規則を追加したいと思います。データ型が不変の場合は、予期しない動作について心配する必要はありません。データ型が変更可能な場合は、予期しない動作が起こらないように、必ずそのコピーを作成してください。

不変のデータ型 :string(タプルの文字)、タプル

可変データ型 :リスト、配列、辞書

1

Pythonは参照で動作するので、dict2 = dict1を実行したときにはdict2への参照を渡します。これはdict1と同じです。したがって、dict1またはdict2に変更を加えると、参照が変更され、両方とも変更がなくなります。英語を間違えたら申し訳ありません。

1
Rodrigo Moraes

記録のためだけに。私は次のコードを使用していますが、これはディープコピーよりも3倍以上速いjson構文に従うdictsにあります

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

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の値も更新しました。 idsidsで参照しているため、blocked_ids my_usersでidsを拡張した場合[])} _。

0