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が浅いコピー。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
list1 = [1, 2, 3, 4]   # オリジナル
list2 = list1 # 参照(reference)
list3 = list1[:] # 浅いコピー(shallow copy)
list4 = list1.copy() # 浅いコピー(shallow copy)
list5 = list(list1) # 浅いコピー(shallow copy)
list1 = [1, 2, 3, 4]   # オリジナル list2 = list1 # 参照(reference) list3 = list1[:] # 浅いコピー(shallow copy) list4 = list1.copy() # 浅いコピー(shallow copy) list5 = list(list1) # 浅いコピー(shallow copy)
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はデータをおいてある場所と言う理解。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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        # 浅いコピー
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        # 浅いコピー
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のまま。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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]    # 浅いコピー
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]    # 浅いコピー
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をオリジナルにする。浅いコピーが別のデータを参照しているのは同じ。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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      # 浅いコピー
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      # 浅いコピー
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を変更。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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]   # 浅いコピー
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]   # 浅いコピー
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)を実行。

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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に変更。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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] のまま
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] のまま
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の要素を変えても同じ。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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]   # 深いコピー
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]   # 深いコピー
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でコピーされていた。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
lst14 = [[[[[[[[[9]]]]]]]]] # オリジナル
lst15 = copy.deepcopy(lst14) # 深いコピー
>>> lst14[0][0][0][0][0][0][0][0][0] = 0
>>> lst14
[[[[[[[[[0]]]]]]]]] # オリジナル
>>> lst15
[[[[[[[[[9]]]]]]]]] # 深いコピー
lst14 = [[[[[[[[[9]]]]]]]]] # オリジナル lst15 = copy.deepcopy(lst14) # 深いコピー >>> lst14[0][0][0][0][0][0][0][0][0] = 0 >>> lst14 [[[[[[[[[0]]]]]]]]] # オリジナル >>> lst15 [[[[[[[[[9]]]]]]]]] # 深いコピー
lst14 = [[[[[[[[[9]]]]]]]]]   # オリジナル
lst15 = copy.deepcopy(lst14)  # 深いコピー
>>> lst14[0][0][0][0][0][0][0][0][0] = 0
>>> lst14
[[[[[[[[[0]]]]]]]]]  # オリジナル
>>> lst15
[[[[[[[[[9]]]]]]]]]  # 深いコピー

取りあえずのまとめ

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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をコピーしました