Python deepcopy()とは?ネストしたリストを完全にコピーする方法

Pythonのdeepcopy()は、リストなどのオブジェクトを内部の要素まで含めて完全にコピーするための関数です。

特に、リストの中にリストが入っている「ネストしたリスト」をコピーするときに役立ちます。

通常のcopy()では、ネストしたリストの内部まで完全にコピーされないため、コピー先を変更すると元のリストまで変わってしまう場合があります。

この記事では、Pythonのdeepcopy()の基本的な使い方から、copy()との違い、ネストしたリストを安全にコピーする方法まで初心者向けに解説します。

Pythonのdeepcopy()とは?

deepcopy()は、Pythonのcopyモジュールに用意されている関数です。

基本的な使い方は次のとおりです。

import copy

コピー先 = copy.deepcopy(コピー元)

例えば、単純なリストをコピーできます。

import copy

numbers = [10, 20, 30]

new_numbers = copy.deepcopy(numbers)

print(new_numbers)

実行結果:

[10, 20, 30]

deepcopy()を使うと、元のリストとは別のオブジェクトとしてコピーできます。

ネストしたリストをdeepcopy()する

deepcopy()が特に重要になるのが、リストの中にリストが入っている場合です。

例えば、次のようなリストです。

numbers = [[1, 2], [3, 4]]

このような構造をネストしたリストと呼びます。

deepcopy()を使ってコピーしてみましょう。

import copy

numbers = [[1, 2], [3, 4]]

new_numbers = copy.deepcopy(numbers)

new_numbers[0][0] = 100

print(numbers)
print(new_numbers)

実行結果:

[[1, 2], [3, 4]]
[[100, 2], [3, 4]]

コピー先のnew_numbersを変更しても、元のnumbersには影響していません。

これがdeepcopy()の大きな特徴です。

copy()ではネストしたリストに注意

通常のcopy()でもリストをコピーできます。

import copy

numbers = [[1, 2], [3, 4]]

new_numbers = copy.copy(numbers)

しかし、ネストしたリストでは注意が必要です。

import copy

numbers = [[1, 2], [3, 4]]

new_numbers = copy.copy(numbers)

new_numbers[0][0] = 100

print(numbers)
print(new_numbers)

実行結果:

[[100, 2], [3, 4]]
[[100, 2], [3, 4]]

コピー先のnew_numbersを変更したにもかかわらず、元のnumbersまで変更されています。

これは、copy()が浅いコピー(shallow copy)だからです。

deepcopy()とcopy()の違い

copy()とdeepcopy()の違いを簡単にまとめると次のようになります。

方法 コピーする範囲 ネストしたリスト
copy.copy() 外側のオブジェクト 内部オブジェクトは共有
copy.deepcopy() 内部のオブジェクトまで 独立してコピー

例えば、

import copy

numbers = [[1, 2], [3, 4]]

shallow = copy.copy(numbers)
deep = copy.deepcopy(numbers)

という場合、

numbers
 ├─ [1, 2]
 └─ [3, 4]

shallow
 ├─ [1, 2] ← 共有
 └─ [3, 4] ← 共有

deep
 ├─ [1, 2] ← 別オブジェクト
 └─ [3, 4] ← 別オブジェクト

という違いがあります。

deepcopy()なら3重のネストもコピーできる

deepcopy()は、さらに深いネストにも対応できます。

import copy

data = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

new_data = copy.deepcopy(data)

new_data[0][0][0] = 100

print(data)
print(new_data)

実行結果:

[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
[[[100, 2], [3, 4]], [[5, 6], [7, 8]]]

元のdataには変更が反映されません。

このように、deepcopy()は内部にさらにリストや辞書などが入っている複雑なデータ構造をコピーするときに便利です。

deepcopy()の基本構文

deepcopy()の構文は次のとおりです。

copy.deepcopy(オブジェクト)

例えば、

import copy

new_data = copy.deepcopy(data)

と書きます。

deepcopy()はcopyモジュールに含まれているため、最初にインポートが必要です。

import copy

deepcopy()は元のリストを変更しない

deepcopy()を使って作成したコピーは、元のオブジェクトとは独立しています。

import copy

original = [[10, 20], [30, 40]]

copied = copy.deepcopy(original)

copied[0].append(50)

print(original)
print(copied)

実行結果:

[[10, 20], [30, 40]]
[[10, 20, 50], [30, 40]]

copiedだけが変更されています。

deepcopy()と代入の違い

次のような代入では、リストそのものをコピーしているわけではありません。

numbers = [10, 20, 30]

new_numbers = numbers

この場合、numbersとnew_numbersは同じリストを参照します。

new_numbers.append(40)

print(numbers)

実行結果:

[10, 20, 30, 40]

一方、deepcopy()なら別のオブジェクトになります。

import copy

numbers = [10, 20, 30]

new_numbers = copy.deepcopy(numbers)

new_numbers.append(40)

print(numbers)
print(new_numbers)

実行結果:

[10, 20, 30]
[10, 20, 30, 40]

deepcopy()はいつ使う?

deepcopy()は、コピーしたデータを変更しても元のデータに影響させたくない場合に使います。

特に次のようなケースで便利です。

  • ネストしたリストをコピーする

  • リストの中に辞書が入っている

  • 辞書の中にリストが入っている

  • 複雑なデータ構造を複製する

  • 元データを保存したままコピー側を変更する

例えば、次のようなデータです。

data = [
    {"name": "田中", "scores": [80, 90]},
    {"name": "佐藤", "scores": [70, 85]}
]

このようなデータを完全に独立させてコピーしたい場合にdeepcopy()が役立ちます。

import copy

new_data = copy.deepcopy(data)

deepcopy()とスライスの違い

リストでは、スライスを使ってコピーすることもできます。

numbers = [10, 20, 30]

new_numbers = numbers[:]

しかし、これも基本的には浅いコピーです。

ネストしたリストでは、

numbers = [[1, 2], [3, 4]]

new_numbers = numbers[:]

new_numbers[0][0] = 100

print(numbers)

実行結果:

[[100, 2], [3, 4]]

元のリストにも変更が反映されます。

ネストしたリストを独立させたい場合は、

import copy

new_numbers = copy.deepcopy(numbers)

とします。

deepcopy()とlist.copy()の違い

リストにはcopy()メソッドもあります。

numbers = [10, 20, 30]

new_numbers = numbers.copy()

これは便利ですが、list.copy()も浅いコピーです。

つまり、

numbers = [[1, 2], [3, 4]]

new_numbers = numbers.copy()

new_numbers[0][0] = 100

とすると、元のnumbersにも変更が反映されます。

ネストしたリストを完全にコピーしたい場合は、

import copy

new_numbers = copy.deepcopy(numbers)

を使います。

deepcopy()の注意点

deepcopy()は便利ですが、常に使えばよいというわけではありません。

単純なリストなら、

numbers = [1, 2, 3]

のようなデータであれば、通常のコピー方法でも十分です。

例えば、

new_numbers = numbers.copy()

で問題ありません。

deepcopy()は内部のオブジェクトまでコピーするため、複雑なデータ構造では処理やメモリ使用量が大きくなる場合があります。

そのため、

ネストしたオブジェクトまで独立させたいときにdeepcopy()を使う

と覚えるとよいでしょう。

元の変数まで変わってしまうときは

代入やコピーのあとに元のデータまで変わってしまう原因と対策は、次の記事で症状別にまとめています。

Python deepcopy()まとめ

Pythonのdeepcopy()は、オブジェクトを内部のオブジェクトまで含めて独立した形でコピーするための関数です。

基本的な使い方は、

import copy

new_data = copy.deepcopy(data)

です。

特に重要なのは、copy()との違いです。

copy()
↓
浅いコピー
↓
内部オブジェクトを共有する場合がある

deepcopy()
↓
深いコピー
↓
内部オブジェクトもコピーする

そのため、ネストしたリストを完全にコピーしたい場合はdeepcopy()が便利です。

import copy

original = [[1, 2], [3, 4]]

copied = copy.deepcopy(original)

copied[0][0] = 100

print(original)
print(copied)

実行結果:

[[1, 2], [3, 4]]
[[100, 2], [3, 4]]

「元のリストを残したまま、ネストしたデータを自由に変更したい」ならdeepcopy()と覚えておきましょう。

Pythonの関数・メソッドの全体像はPython基本関数・メソッド完全一覧にまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら