【Python】多次元リストのコピー

Pythonの多次元リストのコピーについて、その挙動が自分にとっては理解しづらいので実験結果とともに整理。
結論から言うと、`copy()`、`リスト[:]`、copyライブラリの`copy.copy()`は浅いコピー(shallow copy)であり、新たな多次元リストを作成し、その後元のリスト中に見つかったリストに対する参照を挿入する。
copyライブラリの`copy.deepcopy()`は深いコピー(deep copy)であり、新たな多次元リストを作成し、その後元のリスト中に見つかったリストのコピーを挿入する。

###`copy()`
「1層目」というのがいいのか、「1次元目」というのがいいのかよくわからないが…。コピーによって別の多次元リストができるので、IDは当然別のものになる。しかしそのリストの中身は参照なので、コピーの1層目を書き換えてもオリジナルのリストにその影響はないが、2層目以降の書き換えはIDが違うのにもかかわらずオリジナルにも波及する。

```python:copy()
lst = [[0]*3 for _ in range(3)]

lst1 = lst.copy()

lst1[1] = ['r'] * 3
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))

lst1[0][1] = 'r'
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))
```
```:出力
lst1 =  [[0, 0, 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139730947400512
lst =  [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139730946890944
lst1 =  [[0, 'r', 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139730947400512
lst =  [[0, 'r', 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139730946890944
```

###`リスト[:]`
`copy()`と同様。

```python:リスト[:]
lst = [[0]*3 for _ in range(3)]

lst1 = lst[:]

lst1[1] = ['r'] * 3
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))

lst1[0][1] = 'r'
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))
```
```:出力
lst1 =  [[0, 0, 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 140313735131456
lst =  [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 140313734622336
lst1 =  [[0, 'r', 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 140313735131456
lst =  [[0, 'r', 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 140313734622336
```

###copyライブラリ`copy.copy()`
`copy()`と同様。

```python:copy.copy()
import copy

lst = [[0]*3 for _ in range(3)]

lst1 = copy.copy(lst)

lst1[1] = ['r'] * 3
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))

lst1[0][1] = 'r'
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))
```
```:出力
lst1 =  [[0, 0, 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139783910001664
lst =  [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139783910057280
lst1 =  [[0, 'r', 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139783910001664
lst =  [[0, 'r', 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139783910057280
```

###copyライブラリ`copy.deepcopy()`
コピーによって別の多次元リストができるのでIDは別のものになり、そのリストの中身もコピーされる。いわば、クローンが作られる。全く同じ内容だが別のものなので、それを書き換えてもオリジナルには影響がない。

```python:copy.deepcopy()
import copy

lst = [[0]*3 for _ in range(3)]

lst1 = copy.deepcopy(lst)

lst1[1] = ['r'] * 3
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))

lst1[0][1] = 'r'
print('lst1 = ', lst1)
print('id(lst1) = %d' % id(lst1))
print('lst = ', lst)
print('id(lst) = %d' % id(lst))
```
```:出力
lst1 =  [[0, 0, 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139629482679488
lst =  [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139629482735040
lst1 =  [[0, 'r', 0], ['r', 'r', 'r'], [0, 0, 0]]
id(lst1) = 139629482679488
lst =  [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
id(lst) = 139629482735040
```

###参考
[Python - リストを複製する](https://murashun.jp/blog/20200113-22.html)
[copy --- 浅いコピーおよび深いコピー操作](https://docs.python.org/ja/3/library/copy.html)
[【Python】 自分で作った関数の引数にリストを代入すると、そのオリジナルのリストが改変されてしまうのを防ぐ方法](https://qiita.com/somolyot/questions/1934c439428bf3bd24d3)