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まで別データとしてコピーする。
コメント