Python: listのcopyについての備忘録

Python

listのコピーについて

以前下記エントリでlistのコピー(参照(reference)ではない浅いコピー(shallow copy))には
m = p[ : ](←p[ : ]はリストpの全要素を切り出す(slice)するコード)
と書いたけれど、結構忘れがち。

調べたら色々な方法があるので、listの参照(reference)、浅いコピー(shallow copy)、深いコピー(deep copy))をまとめる。

まずは参照と浅いコピーから

参照(reference)と浅いコピー(shallow copy)

5つのlistでテスト

list1を作り、参照、コピーする為に4つの方法”=”、”= p[ : ]”、”copy()”、”list”()のそれぞれを使ってlist2、list3、list4、list5を作る。
list2が参照、list3、list4、list5が浅いコピー。

list1 = [1, 2, 3, 4]   # オリジナル
list2 = list1            # 参照(reference)
list3 = list1[:]         # 浅いコピー(shallow copy)
list4 = list1.copy()     # 浅いコピー(shallow copy)
list5 = list(list1)      # 浅いコピー(shallow copy)

データとid

それぞれの中のデータとidを出力。idはデータをおいてある場所と言う理解。

list1
[1, 2, 3, 4]
list2
[1, 2, 3, 4]
list3
[1, 2, 3, 4]
list4
[1, 2, 3, 4]
list5
[1, 2, 3, 4]

print(id(list1))
4486086208             # オリジナル
print(id(list2))
4486086208       # 参照
print(id(list3))
4488507776        # 浅いコピー
print(id(list4))
4486817344        # 浅いコピー
print(id(list5))
4488563136        # 浅いコピー

それぞれ中身のデータは同じ、idについてはオリジナルと参照は同じものを見ており、浅いコピーは全て違う場所に置いてある別のデータを見ているのが分かる。

参照と浅いコピーの違い

オリジナルであるlist1の要素の1つ目を6に変更すると、参照(reference)のlist2の1つ目の要素も同様に6になる。

浅いコピー(shallow copy)であるlist3, list4, list5では元の1のまま。

list1[0] = 6      # 一番目の要素"1"を6に変更

list1
[6, 2, 3, 4]      # オリジナル
list2
[6, 2, 3, 4]   # 参照 list1を参照しているだけなので、同様に6に置き換わっている
list3
[1, 2, 3, 4]    # 浅いコピー 別のリストへコピーされているので、元の[1, 2, 3, 4]のまま
list4
[1, 2, 3, 4]    # 浅いコピー
list5
[1, 2, 3, 4]    # 浅いコピー

listの中にlistを持つlistのコピー

今度はlistの要素としてlistを持つlist6をオリジナルにする。浅いコピーが別のデータを参照しているのは同じ。

list6 = [[1, 2], 3, 4] # オリジナル
list7 = list6      # 参照
list8 = list6.copy()  # 浅いコピー
list9 = list(list6)   # 浅いコピー

id(list6)
4488481152              # オリジナル
id(list7)
4488481152           # 参照
id(list8)
4488563968            # 浅いコピー
id(list9)
4489343232           # 浅いコピー

浅いコピーではlist内listは元のデータを参照する

list6の2つ目の要素(リスト[1, 2]がlist6の1つ目の要素なので、2つ目の要素は数字3)と、1つ目の要素リスト[1, 2]の中の1つ目の要素数字1を変更。

list6[1] = 7    # 2つ目の要素を7に変える
list6[0][0] = 6   # 1つ目の要素リスト[1, 2]の中の1つ目の要素を6に変える

list6
[[6, 2], 7, 4]    # オリジナル
list7
[[6, 2], 7, 4]   # 参照
list8
[[6, 2], 3, 4]   # 浅いコピー list内list[1, 2]は参照なので変わっている
list9
[[6, 2], 3, 4]   # 浅いコピー

list8, list9は両方とも2つ目の要素3は変わっていないが、1つ目の要素リスト[1, 2] の1は6に変わっている。

list内listまで変わらないように(参照にならない様に)コピーする為には深いコピー(deep copy)でのコピーが必要。

深いコピー(deep copy)

モジュールcopyをインポート

モジュールをインポートしてcopy.deepcopy()を使用する事で深いコピー(deep copy)を実行。

コピーはどちらも別のデータを参照している。

import copy

list6 = [[1, 2], 3, 4]         # オリジナル
list7 = list6                  # 参照
list8 = list6.copy()           # 浅いコピー(shallow copy)
list9 = copy.deepcopy(list6)   # 深いコピー(deep copy)

id(list6)
4452450880      # オリジナル
id(list7)
4452450880     # 参照(reference)
id(list8)
4451667072    # 浅いコピー(shallow copy)
id(list9)
4451584512    # 深いコピー(deep copy)

同じ様にlist6の2つ目の要素(リスト[1, 2]がlist6の1つ目の要素なので、2つ目の要素は数字3)を7に、1つ目の要素リスト[1, 2]の中の1つ目の要素数字1を6に変更。

list6[1] = 7    # 2つ目の要素を7に変える
list6[0][0] = 6  # 1つ目の要素リスト[1, 2]の中の1つ目の要素を6に変える

list6
[[6, 2], 7, 4]   # オリジナル
list7
[[6, 2], 7, 4]  # 参照
list8
[[6, 2], 3, 4]   # 浅いコピー ⇒ list内list[1, 2]はlist6を参照しているので、[6, 2]に変わっている
list9
[[1, 2], 3, 4]   # 深いコピー ⇒ [[1, 2], 3, 4] のまま

深いコピーのlist9はdeepcopyでコピーした元のままになっている。deep copyが出来た。

ちなみに、copy.deepcopy()の代わりにcopy.copy()で浅いコピー(shallow copy)ができる。

もう一つ深いlist

先ほどより深いリストを作り、list内list内listの要素を変えても同じ。

list10 = [[[1, 2], 3], 4]        # オリジナル
list11 = list10                  # 参照
list12 = list10.copy()           # 浅いコピー
list13 = copy.deepcopy(list10)  # 深いコピー

list10[0][0][0] = 7
list10[1] = 8

list10
[[[7, 2], 3], 8]    # オリジナル
list11
[[[7, 2], 3], 8]  # 参照
list12
[[[7, 2], 3], 4]  # 浅いコピー
list13
[[[1, 2], 3], 4]   # 深いコピー

もっともっと深いlist

もっともっと深いlistの要素もdeep copyでコピーされていた。

lst14 = [[[[[[[[[9]]]]]]]]]   # オリジナル
lst15 = copy.deepcopy(lst14)  # 深いコピー
>>> lst14[0][0][0][0][0][0][0][0][0] = 0
>>> lst14
[[[[[[[[[0]]]]]]]]]  # オリジナル
>>> lst15
[[[[[[[[[9]]]]]]]]]  # 深いコピー

取りあえずのまとめ

現時点で自分が理解しているlistの参照、浅いコピー、深いコピーは下記7種類。

import copy
original = [1, 2, 3, 4]           # オリジナル
reference = list1                 # 参照(reference)
shallowcopy1 = list1[:]           # 浅いコピー(shallow copy)
shallowcopy2 = list1.copy()       # 浅いコピー(shallow copy)
shallowcopy3 = list(list1)        # 浅いコピー(shallow copy)
shallowcopy4 = copy.copy(list1)   # 浅いコピー(shallow copy)
deepcopy = copy.deepcopy(list1)   # 深いコピー(deep copy)

参照は元データが変われば参照しているlistの中身も変わる。

浅いコピーはlistについては別データとしてコピーするが、list内listは参照になる。

深いコピーはlist内listまで別データとしてコピーする。

コメント

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