Pythonの「浅いコピー」と「深いコピー」の違いとは?コピーの罠を徹底解説!
こんにちは。ゆうせいです。
プログラミングをしていると、「データを複製したい」場面ってよくありますよね。特にPythonでリストや辞書を扱うとき、元のデータを残しつつ、新しいデータで何か試したい…なんて考えるはずです。
「なんだ、簡単だよ。=(イコール)で代入すればいいんでしょ?」
そう思ったあなた、実はそこに大きな落とし穴があるかもしれません!
今日は、Pythonプログラミングで多くの初心者がつまずきやすい、「shallow copy(浅いコピー)」と「deep copy(深いコピー)」という重要な概念について、一緒に学んでいきましょう。
「コピー」の前に…「代入」がコピーじゃない?
まず、一番やってしまいがちな「代入」を見てみましょう。
original_list = [1, 2, [10, 20]]
copied_list = original_list
これで copied_list が original_list の複製になったように見えます。
では、copied_list の中身を変えてみたらどうなるでしょう?
copied_list[0] = 99
copied_list[2][0] = 999
print(f"コピーしたリスト: {copied_list}")
print(f"元のリスト: {original_list}")
実行結果はこうなります。
コピーしたリスト: [99, 2, [999, 20]]
元のリスト: [99, 2, [999, 20]]
あれ? copied_list を変えただけなのに、original_list まで変わってしまいました!
これはなぜでしょうか?
実は、Pythonの = での代入は、データを丸ごと複製しているわけではないんです。
original_list というのは、データ([1, 2, [10, 20]])が保存されている「場所の住所」を持っているようなものだと想像してください。
copied_list = original_list と書くことは、「copied_list さんにも、original_list さんが知っているのと同じ住所を教えます」という意味になります。
つまり、二人は同じ家(データ)を見ている状態です。
だから、copied_list さんが家の壁紙を変えたら([0] = 99)、original_list さんが家を見ても、当然壁紙は変わっていますよね。
これを「参照(リファレンス)を渡している」と呼びます。これでは独立したコピーとは言えません。
Shallow Copy(浅いコピー)とは?
「じゃあ、ちゃんと別の家(データ)を作りたい!」
そのために使うのが copy モジュールです。まずは「浅いコピー」から見ていきましょう。
copy.copy() を使います。
import copy
original_list = [1, 2, [10, 20]]
shallow_copied_list = copy.copy(original_list)
これは、「original_list とそっくりな、新しい家を建ててください」とお願いするイメージです。
では、さっきと同じように中身を変えてみましょう。
shallow_copied_list[0] = 99
print(f"浅いコピー: {shallow_copied_list}")
print(f"元のリスト: {original_list}")
実行結果:
浅いコピー: [99, 2, [10, 20]]
元のリスト: [1, 2, [10, 20]]
おお!今度は shallow_copied_list[0] を変えても、original_list[0] は変わりませんでした。新しい家が建った証拠です。
…しかし、油断は禁物です。
original_list の中には、さらに別のリスト [10, 20] が入っていましたね。
この「リストの中のリスト」を変えたらどうなるでしょう?
shallow_copied_list[2][0] = 999
print(f"浅いコピー: {shallow_copied_list}")
print(f"元のリスト: {original_list}")
実行結果:
浅いコピー: [99, 2, [999, 20]]
元のリスト: [1, 2, [999, 20]]
なんと!またしても元のリストが変わってしまいました!
これが「浅い」と呼ばれる理由です。
Shallow Copyは、例えるなら「家の間取りや家具(1 とか 2)は新しく用意するけど、家の中にある『金庫』は、元の家にある『金庫』の場所(住所)だけを教えておく」というコピー方法なんです。
[1, 2, [10, 20]] の [10, 20] の部分が、この「金庫」にあたります(専門用語では「ミュータブルなオブジェクト」と言います)。
shallow_copied_list は新しい家(リスト)ですが、その中にある「金庫の住所」は元のリストが持っている「金庫の住所」と全く同じ。
だから、どちらか一方から金庫の中身([10, 20] の 10)を変えると、もう一方から見ても変わってしまうのです。
Deep Copy(深いコピー)とは?
「もう!とにかく全部、完璧に複製して、お互い一切影響しないようにしたい!」
そんなときに登場するのが「深いコピー」、copy.deepcopy() です。
import copy
original_list = [1, 2, [10, 20]]
deep_copied_list = copy.deepcopy(original_list)
Deep Copyは、「家の間取りも家具も、そして家の中にある『金庫』も、その金庫の中身さえも!すべて丸ごと新しく作ってください」とお願いする方法です。
元のデータと全く同じものが、全く別の場所に、新しく作られます。
では、試してみましょう。
deep_copied_list[0] = 99
deep_copied_list[2][0] = 999
print(f"深いコピー: {deep_copied_list}")
print(f"元のリスト: {original_list}")
実行結果:
深いコピー: [99, 2, [999, 20]]
元のリスト: [1, 2, [10, 20]]
やりました!
今度こそ、deep_copied_list のどこを変更しても、original_list には一切影響がありません。
これこそが、私たちが最初にイメージしていた「完全な複製」ですね。
どう使い分ける?メリットとデメリット
では、いつでも Deep Copy を使えば良いのでしょうか?そうとも限りません。
Shallow Copy(浅いコピー)
- メリット: Deep Copyに比べて処理が速く、メモリの使用量も少ないです。コピーするのが「浅い」階層だけだからですね。
- デメリット: 今見たように、リストの中にリストがあるような複雑なデータ(入れ子構造)だと、意図せず元のデータを変更してしまう危険があります。
- 使いどき: リストの中身が数値や文字列だけ(金庫がない状態)で、入れ子になっていない場合は、Shallow Copyで十分かつ高速です。
Deep Copy(深いコピー)
- メリット: 元のデータと完全に切り離された、独立した複製を作れます。これが最大の強み。安心して複製側を変更できます。
- デメリット: データが複雑になればなるほど、すべてを新しく作り直すため、処理に時間がかかり、メモリも多く消費します。
- 使いどき: リストの中に別のリストや辞書が入っている場合や、元のデータに絶対に影響を与えたくない場合に必須です!
まとめと今後の学習
今回は、Pythonのコピーの仕組み、「参照(代入)」「浅いコピー」「深いコピー」について学びました。
=での代入は、コピーではなく「同じ住所(参照)を見る」だけ。copy.copy()(浅いコピー) は、一番外側だけ新しく作る。中身(入れ子)は同じ住所を見る。copy.deepcopy()(深いコピー) は、中身の奥深くまで、すべてを新しく作る。
この違いを理解できると、データが意図せず書き換わってしまうバグを劇的に減らせますよ。
「なんだか難しかったな…」と感じたかもしれません。
でも大丈夫、この感覚はとても大切です。
今後さらに理解を深めるためには、「ミュータブル (Mutable)なオブジェクト」と「イミュータブル (Immutable)なオブジェクト」という概念を学ぶことを強くおすすめします。
簡単に言うと、「中身を変えられるデータ(リストや辞書など)」がミュータブルで、「中身を変えられないデータ(数値や文字列など)」がイミュータブルです。
今回の「金庫」の例えは、まさにミュータブルなオブジェクトのことだったんです。
この区別がつくようになると、「あ、このデータは浅いコピーで十分だな」「これは深いコピーじゃないと危ない!」と自分で判断できるようになります。
まずは、copy モジュールを使うときは、自分が扱っているデータに入れ子構造(リストの中のリストなど)がないか確認するクセをつけましょう!
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール
- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。
最新の投稿
山崎講師2025年10月26日Pythonのimport完全ガイド!パッケージとモジュールの階層構造をスッキリ理解しよう
山崎講師2025年10月26日Python関数の引数の罠!正しい順番をマスターしてエラーを回避しよう
山崎講師2025年10月26日Pythonの「浅いコピー」と「深いコピー」の違いとは?コピーの罠を徹底解説!
山崎講師2025年10月26日VSCodeを自分好みに育てる!新人エンジニアのためのsettings.json徹底ガイド