Pythonのコピーをマスターする:copy()とdeepcopy()の違いを徹底解説


 

Pythonでリストや辞書などのミュータブル(変更可能)なオブジェクトを扱う際、「コピー」の概念を理解することは非常に重要です。安易なコピーは、予期せぬデータの変更につながることがあります。この記事では、Pythonにおける**浅いコピー(copy()深いコピー(deepcopy())**の違いを、具体的なサンプルコードを交えながらわかりやすく解説します。


 

なぜコピーを理解する必要があるのか?

 

Pythonでは、変数にオブジェクトを代入すると、そのオブジェクトの「参照」がコピーされます。つまり、新しい変数も同じオブジェクトを指すことになります。

Python
 
original_list = [1, 2, 3]
copied_reference = original_list # 参照をコピー

copied_reference.append(4) # copied_reference を変更

print(original_list)
# 出力: [1, 2, 3, 4]
# original_list も変更されてしまう!

このように、参照コピーでは、一方を変更するともう一方も影響を受けてしまいます。これを避けるためには、独立したコピーを作成する必要があります。ここで登場するのが、浅いコピーと深いコピーです。


 

浅いコピー(Shallow Copy):copy.copy()

 

浅いコピーは、元のオブジェクトの「表層」のみをコピーします。つまり、新しいコンテナ(リストや辞書など)は作成されますが、そのコンテナ内に含まれる要素(オブジェクト)は、元のオブジェクトと同じものを参照します。

copyモジュールのcopy()関数を使用するか、リストの場合はスライス[:]list()コンストラクタを使うことで浅いコピーを作成できます。

 

copy.copy()の基本

 

Python
 
import copy

original_list = [1, [2, 3], 4]
shallow_copied_list = copy.copy(original_list)

print(f"オリジナル: {original_list}")
print(f"浅いコピー: {shallow_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3], 4]
# 浅いコピー: [1, [2, 3], 4]

# 1. 浅いコピーの「ミュータブルでない要素」を変更
shallow_copied_list[0] = 99
print(f"\n変更後(ミュータブルでない要素):")
print(f"オリジナル: {original_list}")
print(f"浅いコピー: {shallow_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3], 4]  # 影響なし
# 浅いコピー: [99, [2, 3], 4]

# 2. 浅いコピーの「ネストされたミュータブルな要素」を変更
shallow_copied_list[1].append(5) # ネストされたリストを変更
print(f"\n変更後(ネストされたミュータブルな要素):")
print(f"オリジナル: {original_list}")
print(f"浅いコピー: {shallow_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3, 5], 4]  # オリジナルも変更されてしまう!
# 浅いコピー: [99, [2, 3, 5], 4]

上記の例からわかるように、浅いコピーでは:

  • コピーしたリスト自体の要素を変更しても、元のリストのイミュータブル(変更不可能)な要素は影響を受けません。

  • しかし、コピーしたリスト内のネストされたミュータブルな要素(この場合は内側のリスト[2, 3])を変更すると、元のリストのその要素も影響を受けてしまいます。なぜなら、両者が同じ内側のリストを参照しているからです。

 

リスト特有の浅いコピー方法

 

リストの場合、copy.copy()以外にも浅いコピーを作成する方法があります。

Python
 
original_list = [1, [2, 3], 4]

# スライス [:] を使う方法
shallow_copy_slice = original_list[:]

# list() コンストラクタを使う方法
shallow_copy_constructor = list(original_list)

print(f"スライスでコピー: {shallow_copy_slice}")
print(f"list()でコピー: {shallow_copy_constructor}")
# これらも copy.copy() と同じく浅いコピーです

 

深いコピー(Deep Copy):copy.deepcopy()

 

深いコピーは、元のオブジェクトとその中に含まれるすべてのオブジェクト(ネストされたオブジェクトも含む)を再帰的にコピーします。これにより、元のオブジェクトと完全に独立した新しいオブジェクトが作成され、一方を変更してももう一方に影響を与えることはありません。

copyモジュールのdeepcopy()関数を使用します。

Python
 
import copy

original_list = [1, [2, 3], 4]
deep_copied_list = copy.deepcopy(original_list)

print(f"オリジナル: {original_list}")
print(f"深いコピー: {deep_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3], 4]
# 深いコピー: [1, [2, 3], 4]

# 1. 深いコピーの「ミュータブルでない要素」を変更
deep_copied_list[0] = 99
print(f"\n変更後(ミュータブルでない要素):")
print(f"オリジナル: {original_list}")
print(f"深いコピー: {deep_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3], 4] # 影響なし
# 深いコピー: [99, [2, 3], 4]

# 2. 深いコピーの「ネストされたミュータブルな要素」を変更
deep_copied_list[1].append(5) # ネストされたリストを変更
print(f"\n変更後(ネストされたミュータブルな要素):")
print(f"オリジナル: {original_list}")
print(f"深いコピー: {deep_copied_list}")
# 出力例:
# オリジナル: [1, [2, 3], 4] # オリジナルは変更されない!
# 深いコピー: [1, [2, 3, 5], 4]

深いコピーでは、ネストされたミュータブルな要素を変更しても、元のオブジェクトには一切影響がないことがわかります。

 

deepcopy()の使用に関する注意点

 

  • パフォーマンス: deepcopy()は、すべてのネストされたオブジェクトを再帰的にコピーするため、処理に時間がかかり、メモリを多く消費する可能性があります。大規模なデータや非常に深いネストを持つオブジェクトに対して使用する場合は、パフォーマンスに注意が必要です。

  • 循環参照: deepcopy()は、オブジェクト間に循環参照(AがBを参照し、BがAを参照するような構造)があっても正しく処理できます。


 

どちらのコピーを使うべきか?

 

状況に応じて、浅いコピーと深いコピーを使い分ける必要があります。

  • 浅いコピー (copy.copy()[:]list()など):

    • リストや辞書の直下の要素がすべて**イミュータブル(数値、文字列、タプルなど)**である場合。

    • ネストされたミュータブルな要素があるが、それらを変更する予定がない、あるいは意図的に共有したい場合。

    • パフォーマンスが非常に重要な場合。

  • 深いコピー (copy.deepcopy()):

    • リストや辞書の中にミュータブルなオブジェクト(リスト、辞書、カスタムオブジェクトなど)がネストされており、それらを変更しても元のオブジェクトに絶対に影響を与えたくない場合。

    • データの完全な独立性を確保したい場合。


 

まとめ

 

Pythonでオブジェクトをコピーする際には、参照コピー、浅いコピー、深いコピーの3つの概念を理解することが不可欠です。

  • 参照コピー: 変数を代入するだけで、同じオブジェクトを指す。

  • 浅いコピー (copy.copy(), [:], list()): 新しいコンテナを作成するが、ネストされたミュータブルな要素は元のオブジェクトと共有する。

  • 深いコピー (copy.deepcopy()): 元のオブジェクトとネストされたすべてのオブジェクトを完全に独立してコピーする。

これらの違いを正しく把握し、状況に応じた適切なコピー方法を選択することで、Pythonプログラムのバグを防ぎ、データの整合性を保つことができます。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座