はじめに
Python3.8.3で実行した結果を記載しています。
リストのコピー
「リストのコピーを作り、値を変えて元のものと比較したい」と思うことがある。その場合、次のようにcopyモジュールのdeepcopyメソッドを使って書く必要がある。
1次元リスト
リストが1次元のときは、deepcopy()
、スライス、list()
などでコピーできる。
import copy
list_1 = [0,1,2]
list_2 = copy.deepcopy(list_1) #deepcopy()を使う
list_3 = list_1[:] #元のリストをスライスする
list_4 = list(list_1) #list()を使う
list_2[0] = 1000
list_3[0] = 2000
list_4[0] = 3000
print(list_1)
print(list_2)
print(list_3)
print(list_4)
出力はこのようになる。
[0, 1, 2]
[1000, 1, 2]
[2000, 1, 2]
[3000, 1, 2]
2次元リスト
リストが2次元(以上)のときは、deepcopy()
でコピーできる。スライスやlist()
ではうまくいかない(後述)。
import copy
list_1 = [[0,1,2], [3,4,5]]
list_2 = copy.deepcopy(list_1)
list_2[0][0] = 1000
print(list_1)
print(list_2)
出力はこのようになる。
[[0, 1, 2], [3, 4, 5]]
[[1000, 1, 2], [3, 4, 5]]
うまくいかないパターン
=で代入
リストの参照自体をコピーしているため、元のリストの値も変更されてしまう。
# =で代入する
list_1 = [0,1,2,3]
list_2 = list_1
list_2[0] = 1000
print(list_1)
print(list_2)
出力
[1000, 1, 2, 3]
[1000, 1, 2, 3]
スライス、list( )でコピー
Pythonのリストで参照渡しでないコピーを作成する話 には、スライスでコピーするのが手軽だとあるが、これはリストが1次元のときのみうまくいく。
リストが2次元以上になると、次のようになる。途中で参照渡しが起こるのでうまくいかないようだ。
# 元のリストをスライスでコピーする(2次元)
list_3 = [[0,1,2], [3,4,5]]
list_4 = list_3[:]
list_4[0][0] = 1000
print(list_3)
print(list_4)
出力
[[1000, 1, 2], [3, 4, 5]]
[[1000, 1, 2], [3, 4, 5]]
また、スライスの代わりにlist()
メソッドを使っても同じ結果になる。
解説
リストを普通に複製すると、値ではなく参照自体がコピーされる。これを浅いコピーという。逆に、値のみをコピーすることを深いコピーという。Pythonでは、リストや辞書のような複合オブジェクトには、特に指定がない限り浅いコピーが適用される。多次元リストをスライスでコピーしたときに参照渡しになっていたのも、スライス内部での2次元目のコピーが浅いコピーだったからだろう。
そして、copy.deepcopy()
メソッドは「深いコピーをして」という指示である。これを使うと、多次元リストのコピーでも、すべての要素に深いコピーをしてくれる。
詳しくは、公式のcopy --- 浅いコピーおよび深いコピー操作を参照。
まとめ
1次元リストならスライス、list()
、copy.deepcopy()
多次元リストならcopy.deepcopy()