[Python] リストの複製、浅いコピーと深いコピー

[Python] リストの複製、浅いコピーと深いコピー

Python のリストをコピーする方法

Python のリストをコピーする方法を調べました。浅いコピー、深いコピー(deepcopy)、それからリスト内包表記やスライスを使った浅いコピーについてまとめます。

リストのコピー

Python でリストの中身をコピーしたい場合、単純に以下のように代入するだけでは参照のコピーとなりうまくいきません。

a = [0, 1, 2, 3, 4]
b = a
b[0] = 100

print(a) # [100, 1, 2, 3, 4]
print(b) # [100, 1, 2, 3, 4]

配列の中身だけをコピーするには、copy ライブラリを使います。

copy — 浅いコピーおよび深いコピー操作 — Python 3.7.4rc2 ドキュメント

浅いコピーと深いコピー

一般にリストデータのコピーには浅いコピーと深いコピーの2種類があります。

浅いコピーは、リストの各要素の値をコピーします。1次元のリストだと問題ないのですが、多次元のリストだとリストの中身の参照がコピーされるため、完全に別物にはなりません。

深いコピーは、リストの中にあるリストについても再帰的にコピーが行います。これによって完全に複製されたリストを手に入れることができます。

具体例を見ていきます。

浅いコピー (shallow copy)

浅いコピーの場合、copy.copy() を使います。引数で渡したオブジェクトの浅い(shallow)コピーを返します。

import copy
a = [0, 1, 2, 3, 4]

# 浅いコピー
b = copy.copy(a)

b[0] = 100
print(a) # [0, 1, 2, 3, 4]
print(b) # [100, 1, 2, 3, 4]

浅いコピーの場合、多次元リストのような入れ子になった複雑なオブジェクトはうまくコピーできません。

import copy
a = [[0, 1, 2], [3, 4, 5]]

b = copy.copy(a)
b[0][0] = 100

print(a) # [[100, 1, 2], [3, 4, 5]
print(b) # [[100, 1, 2], [3, 4, 5]

多次元のリストについては、コピーする値がリストの参照なので、結果同じ配列としてコピーされてしまいます。

浅いコピーは1次元のリストに対してのみ有効なコピーです。

スライスでコピー

copy.copy() をいちいち呼び出すのが面倒な人はスライスで代用しましょう。

a = [0, 1, 2, 3, 4]

# スライスで浅いコピー
b = a[:]

b[0] = 100
print(a) # [0, 1, 2, 3, 4]
print(b) # [100, 1, 2, 3, 4]

そのほかに、リスト内包表記を使ってもコピーできますね。

b = [x for x in a]

深いコピー (deep copy)

深いコピーは多次元リストについても再帰的にすべての値をコピーします。copy.deepcopy() を使います。引数で渡したオブジェクトの要素について、再帰的にコピーした結果を返します。

import copy
a = [[0, 1, 2], [3, 4, 5]]

# 深いコピー
b = copy.deepcopy(a)
b[0][0] = 100

print(a) # [[0, 1, 2], [3, 4, 5]
print(b) # [[100, 1, 2], [3, 4, 5]

深いコピーを実行すると、コピーした多次元リストの要素を更新してもコピー元のリストに影響しないことがわかります。

当然コピー処理にかかるコストは浅いコピーに比べて大きくなります。

以上。

Pythonカテゴリの最新記事