目的
この記事を閲覧して,以下のことが分かる事
- list のコピー時の挙動
- シャローコピーとディープコピー
- for 文中の list の挙動
list のコピー
まずはこちらを考えてみましょう.
Q.以下のコードを実行した場合,どのように出力されるでしょうか?
a = [0, 1, 2, 3]
b = a # b に a をコピー
a.append(4) # a に 4 を追加
print(a)
print(b)
選択肢 A
[0, 1, 2, 3, 4]
[0, 1, 2, 3]
選択肢 B
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
A. 選択肢 B
答えは選択肢 B です.
a にだけ 4 を追加したのに何故 b にも 4 が追加されているの?
こう思いませんでしたか?
これ実は,a と b が同じリストになってしまっています.
print(id(a)) # 135764862582848
print(id(b)) # 135764862582848
つまり,a のコピーとして別の b を用意したつもりが,
実はどっちも同じリストだったということになります.
では,どうすれば a のコピーとして別の b を用意できるのか.以下で解説いたします.
copy
copy
をインポートすることで解決できます.
いったんこちらをご覧ください.
import copy
a = [0, 1, 2, 3]
b = copy.copy(a)
a.append(4)
print(a) # [0, 1, 2, 3, 4]
print(id(a)) # 13576486259635
print(b) # [0, 1, 2, 3]
print(id(b)) # 135764862644352
copy を使うことで,a と別のコピー b を用意することができました.
これで a に 4 を追加しても,b には 4 が追加されません.
シャローコピーとディープコピー
上記の通り,copy
を使えば思い通りのコピーができそうだという事がわかりました.
しかし,コピーには
- シャローコピー(浅いコピー)
- ディープコピー(深いコピー)
というものがあります.厄介ですね..
ということでいったんこちらをご覧ください.
import copy
a = [0, 1, 2, 3, [100, 110]]
b = a
c = copy.copy(a) # シャローコピー
d = copy.deepcopy(a) # ディープコピー
a[3] = 4
a[4][0] = 777
print(f"a: {a}, id: {id(a)}, id(a[4]): {id(a[4])}")
print(f"b: {b}, id: {id(b)}, id(b[4]): {id(b[4])}")
print(f"c: {c}, id: {id(c)}, id(c[4]): {id(c[4])}")
print(f"d: {d}, id: {id(d)}, id(d[4]): {id(d[4])}")
出力
a: [0, 1, 2, 4, [777, 110]], id: 135764861728000, id(a[4]): 135764861728448
b: [0, 1, 2, 4, [777, 110]], id: 135764861728000, id(b[4]): 135764861728448
c: [0, 1, 2, 3, [777, 110]], id: 135764861720192, id(c[4]): 135764861728448
d: [0, 1, 2, 3, [100, 110]], id: 135764861724480, id(d[4]): 135764861719744
ざっくり解説
シャローコピー:
新しいコピーを作成する(要素内のリストなどは a と同じところを指す)
リスト内のリストなど深いところまではコピーしない(浅い)
ディープコピー:
完全に新しいコピーを作成する
リスト内のリストなど深いところまでコピーする(深い)
ここまでのまとめ
- a と同じリストを作るときは a = b
- a のコピーを新しく作成し,その後 a と異なる操作をしたい時は copy を使う
- a のコピーで完全に新しいリストを作成したい時はディープコピーする
for 文中のリスト
for 文でリストの要素分ループさせる時,
ループ中にそのリストに要素を追加したらどうなるでしょうか?
最初にループが始まった時の要素分だけでループ?
追加されたらその分追加でループされる?
ということでこちらをご覧ください.
list_a = [0, 1, 2, 3]
for i, a in enumerate(list_a):
print(a)
list_a.append(a+100)
if i >= 6:
break
print(list_a)
出力
0
1
2
3
100
101
102
[0, 1, 2, 3, 100, 101, 102, 103, 200, 201, 202]
答えは,ループ中に追加した分まで追加でループされるでした.
まとめ
- a と同じリストを作るときは a = b
- a のコピーを新しく作成し,その後 a と異なる操作をしたい時は copy を使う
- a のコピーで完全に新しいリストを作成したい時はディープコピーする
- リストのループ中に要素を追加したら,その追加分まで追加でループする