dictionaryのcopyについて
listのcopyを見たので、今回はdictionaryのcopyについて。
最後の方におまけとして、これを書いている途中で新ためて知ったdictionary型データの作り方について。
参照(reference)と浅いコピー(shallow copy)
dictionaryも同様にcopyメソッドを持ち、copyモジュールのcopy.copy()でも、dict()で新しいdictionary型を作る事でも浅いコピーができる。
>>> import copy >>> dic = {'a':1, 'b':2, 'c':3, 'd':4} # オリジナル >>> dic2 = dic # 参照 >>> dic3 = dic.copy() # 浅いコピー >>> dic4 = copy.copy(dic) # 浅いコピー >>> dic5 = dict(dic) # 浅いコピー >>> id(dic) 140276777077208 # オリジナル >>> id(dic2) 140276777077208 # 参照 >>> id(dic3) 140276777078648 # 浅いコピー >>> id(dic4) 140276777077136 # 浅いコピー >>> id(dic5) 140276777070880 # 浅いコピー
浅いコピーはidが違う所から違うデータ。
‘a’のvalueを6に変えると、参照データは変わるが、浅いコピーは変わらないのが分かる。
>>> dic['a'] = 6 # key 'a'のvalueを6に変える >>> dic {'a': 6, 'b': 2, 'c': 3, 'd': 4} # オリジナル >>> dic2 {'a': 6, 'b': 2, 'c': 3, 'd': 4} # 参照 変わったオリジナルを参照 >>> dic3 {'a': 1, 'b': 2, 'c': 3, 'd': 4} # 浅いコピー コピーなので変わらず >>> dic4 {'a': 1, 'b': 2, 'c': 3, 'd': 4} # 浅いコピー dic5 {'a': 1, 'b': 2, 'c': 3, 'd': 4} # 浅いコピー
Valueにlistを持つdictionaryのコピー
listと同様に、dictionaryのvalueとしてlistを持つdic6を準備してコピー。
>>> dic6 = {'a':[1, 2], 'b':[3], 'c':4, 'd':5} # オリジナル >>> dic7 = dic6 # 参照 >>> dic8 = dic6.copy() # 浅いコピー >>> dic9 = dict(dic6) # 浅いコピー
list内の要素を変更してみると、
>>> dic6['a'][0] = 6 # key 'a'の要素list[1, 2]の一つ目の要素1を6に変える >>> dic6['b'][0] = 7 # key 'b'の要素list[3]の一つ目の要素3を7に変える >>> dic6['c'] = 8 # key 'c' の要素4を8に変える >>> dic6 {'a': [6, 2], 'b': [7], 'c': 8, 'd': 5} # オリジナル >>> dic7 {'a': [6, 2], 'b': [7], 'c': 8, 'd': 5} # 参照 >>> dic8 {'a': [6, 2], 'b': [7], 'c': 4, 'd': 5} # 浅いコピー >>> dic9 {'a': [6, 2], 'b': [7], 'c': 4, 'd': 5} # 浅いコピー
cのvalueは変わらないが、浅いコピーなのでlist内の数字は変わったのが分かる。
bについて、例えばdic6[‘b’] = 5の様に、listを丸ごと他の物(この場合int型の5)に変えようとしても変わらない。
あくまでdic6[‘b’][0] = 7の様に、listの中の要素を変える場合のみ、変える事が出来る。
深いコピー
同じdic6で深いコピーを試す。
>>> import copy >>> dic6 = {'a':[1, 2], 'b':[3], 'c':4, 'd':5} >>> dic8 = dict(dic6) # 浅いコピー >>> dic10 = copy.deepcopy(dic4) # 深いコピー >>> dic6['a'][0] = 6 # key 'a'の要素list[1, 2]の一つ目の要素1を6に変える >>> dic6['b'][0] = 7 # key 'b'の要素list[3]の一つ目の要素3を7に変える >>> dic6['c'] = 8 # key 'c' の要素4を8に変える >>> dic6 {'a': [6, 2], 'b': [7], 'c': 8, 'd': 5} # オリジナル >>> dic8 {'a': [6, 2], 'b': [7], 'c': 4, 'd': 5} # 浅いコピー >>> dic10 {'a': [1, 2], 'b': [3], 'c': 4, 'd': 5} # 深いコピー
深いコピーでコピーしたdic10は元のままになっているのが分かる。
dictionaryのコピーのまとめ
reference = dic # 参照 shallowcopy1 = dic.copy() # 浅いコピー shallowcopy2 = copy.copy(dic) # 浅いコピー shallowcopy3 = dict(dic) # 浅いコピー deepcopy = copy.deepcopy(dic4) # 深いコピー
dictionaryのkeyだけをコピーしたい場合
ある辞書型データのkeyだけをコピーしたい場合って結構あると思う。
その場合dict.fromkeys()と言う関数を使えば便利。
>>> dic11 = dict.fromkeys(dic, 0) >>> dic11 {'a': 0, 'b': 0, 'c': 0, 'd': 0} >>> dic12 = dict.fromkeys(dic) >>> dic12 {'a': None, 'b': None, 'c': None, 'd': None}
dict.fromkeys(コピーする辞書型データ, value)で実行すると、引数valueで与えられたデータをそれぞれのvalueとした辞書が出来る(dic11ではvalue=0)。
dic12の様に引数valueを与えなければ、Noneをvalueとしたデータが出来る。
おまけ:辞書型データの作り方
今回の記述にあたり、公式チュートリアルを見て改めて知った辞書データの作り方。
下記全て同じ”{‘one’: 1, ‘two’: 2, ‘three’: 3}”と言う辞書型データを作るコード。
>>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3])) >>> d = dict([('two', 2), ('one', 1), ('three', 3)]) >>> e = dict({'three': 3, 'one': 1, 'two': 2}) >>> f = dict({'one': 1, 'three': 3}, two=2) >>> a == b == c == d == e == f True >>> a {'one': 1, 'two': 2, 'three': 3} >>> b {'one': 1, 'two': 2, 'three': 3} >>> c {'one': 1, 'two': 2, 'three': 3} >>> d {'two': 2, 'one': 1, 'three': 3} >>> e {'three': 3, 'one': 1, 'two': 2} >>> f {'one': 1, 'three': 3, 'two': 2}
d, e, fはKey(’one’, ‘two’, ‘three’)とValue(1, 2, 3)の組み合わせは同じだが順番は異なっている。
これはPythonのバージョン3.7から辞書の挿入順序が保存されている事が影響している理解で、キーの更新は順序には影響が無く、いったん削除されてから再度追加されたキーは末尾に挿入される様になっている。
しかし、”a == b == c == d == e == f”がTrueと言う事は、辞書型データとしては順番が違っても同値と言う事なんだろう。
コメント