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と言う事は、辞書型データとしては順番が違っても同値と言う事なんだろう。

コメント