※記事内に商品プロモーションを含むことがあります。
はじめに
本記事ではリストおよび辞書をコピーする4つの方法について説明する。
Pythonでネストされたリストや辞書をコピーするとき、一方に加えた変更が他方に反映されないようにしたい場合は、copy
モジュールのdeepcopy()
関数を用いる。deepcopy()
関数によって、リスト・辞書の参照先でなく、実体が全てコピーされる。
検証環境はPython 3.7.4である。
リスト・辞書をコピーする方法
Pythonでリスト・辞書をコピーする場合、以下の4つの方法がある(以降ではcopy
モジュールのインポートを省略する)。
1
2
3
4
5
6
7
8
|
import copy
list0 = [0, 1, 2]
list1 = list0 # 1. 直接代入する
list2 = list0.copy() # 2. copy()メソッド
list3 = copy.copy(list0) # 3. copy()関数
list4 = copy.deepcopy(list0) # 4. deepcopy()関数
|
方法1. はデータの参照先のみがコピーされるため、ネストの有無に関わらず、片方へ変更を加えると他方に反映される。
方法2. と3. は等価であり、浅いコピー(shallow copy)と呼ばれる。浅いコピーでは、数値や文字列といったデータ型の実体はコピーされるが、一方、ネストの内側のリスト等は参照先のみコピーされる。(詳細は「ミュータブル」の概念を理解する必要があるが、本記事では扱わない)
そのため、リストまたは辞書がネストされていない場合、片方の変更は他方へ反映されない。しかし、ネストされている場合は、片方の変更が他方へ反映されていまう。
方法4. は深いコピー(deep copy)と呼ばれ、ネストの有無に関わらず、データの実体を全てコピーする。そのため、片方の変更は他方に反映されない。しかし、当然ながら実行速度は遅くなってしまうため、処理速度が重要な場合には浅いコピーで代用できないか考慮する必要がある。
ネストされた配列・辞書のコピー
ネストされた配列・辞書に対して、
でそれぞれコピーを作成する。深いコピーであれば、片方の変更が他方に反映されないことを示す。
浅いコピー
既に上で述べたように、浅いコピーはcopy
モジュールのcopy()
関数や、copy()
メソッドを使って行う。浅いコピーでPythonのネストされたリストや辞書をコピーする場合、一方のリスト(または辞書)に加えた変更が他方にも反映されてしまう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
list1 = [[0, 1], [2, 3]]
list2 = copy.copy(list1)
list1[0][0] = 4 # 0を4に変更
print(list1) # [[4, 1], [2, 3]]
print(list2) # [[4, 1], [2, 3]]
# list2の0も4に変更される
dict1 = {"k0": {"k00": "v00",
"k01": "v01"},
"k1": {"k10": "v10",
"k11": "v11"}}
dict2 = dict1.copy()
dict1["k0"]["k00"] = "v22" # "v00"を変更
print(dict1)
# {'k0': {'k00': 'v22', 'k01': 'v01'}, 'k1': {'k10': 'v10', 'k11': 'v11'}}
print(dict2)
# {'k0': {'k00': 'v22', 'k01': 'v01'}, 'k1': {'k10': 'v10', 'k11': 'v11'}}
# dict2のv00もv22に変更される
|
深いコピー
最後に、copy
モジュールのdeepcopy()
関数を使い、深いコピーでネストされたリストと辞書をコピーする。片方への変更が、他方に反映されないことが分かる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
list1 = [[0, 1], [2, 3]]
list2 = copy.deepcopy(list1)
list1[0][0] = 4 # 0を4に変更
print(list1) # [[4, 1], [2, 3]]
print(list2) # [[0, 1], [2, 3]]
# list2は0のまま
dict1 = {"k0": {"k00": "v00",
"k01": "v01"},
"k1": {"k10": "v10",
"k11": "v11"}}
dict2 = copy.deepcopy(dict1)
dict1["k0"]["k00"] = "v22" # "v00"を変更
print(dict1)
# {'k0': {'k00': 'v22', 'k01': 'v01'}, 'k1': {'k10': 'v10', 'k11': 'v11'}}
print(dict2)
# {'k0': {'k00': 'v00', 'k01': 'v01'}, 'k1': {'k10': 'v10', 'k11': 'v11'}}
# dict2はv00のまま
|
参考
以下の記事を参考にさせていただいた。組み込み関数のid
を使うことによって、データの実体がコピーされたのか、参照先のみコピーされたのかを確認できる。