Python: dictionaryのcopy、dictionary型データの作り方

Python

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

コメント

タイトルとURLをコピーしました