Pythonの浅いコピー(シャローコピー)と深いコピー(ディープコピー)の違いを徹底解説!
Pythonをブラウザで実行しながら実践的に学ぶ
Pythonの基礎からソフトウェアアーキテクチャ,アルゴリズムなどの応用的な内容まで幅広く学べます。
ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。
Pythonを学び始めると、必ずといっていいほど出てくる疑問があります。
「リストや辞書をコピーするときに、なんで思った通りに動かないの?」 「コピーしたつもりが、元のデータまで変わっちゃったんだけど……」
私もプログラミングを始めたころに、まさにこの壁にぶつかりました。 そしてエンジニア歴10年になる今でも、新人の方にコードレビューをするときに同じ質問をされることが本当に多いのです。
この記事では、浅いコピー(シャローコピー)と深いコピー(ディープコピー)の違いを、初心者でも理解しやすい形で徹底的に解説していきます。 サンプルコードや表を交えながら、一緒にコピーの罠を抜け出しましょう。
なぜコピーでつまずくのか?¶
Pythonのリストや辞書は、ただの数値や文字列だけでなく「リストの中にリスト」「辞書の中にリスト」などのように入れ子構造を持つことができます。 この便利さがある一方で、コピーしたときに中身の参照が共有されてしまうという問題が発生します。
「コピペしたのに、なんで一方を変えるともう一方まで変わるの?」
そんな疑問を持ったことがある人は多いはずです。
ここで出てくるのが 浅いコピー と 深いコピー です。
浅いコピー(シャローコピー)とは?¶
まず浅いコピー(シャローコピー)について解説します。
浅いコピーは、一番外側の入れ物(リストや辞書)だけを新しく作るコピー方法です。 ただし、中に入っている要素(特にリストや辞書のようなオブジェクト)はコピーされずに、参照(アドレス)だけがコピーされる点が特徴です。
例を見てみましょう。
import copy
list_a = [[1, 2], [3, 4]]
list_b = copy.copy(list_a) # 浅いコピー
print("コピー直後:")
print("list_a:", list_a)
print("list_b:", list_b)
list_b[0][0] = 99 # 内側のリストを変更
print("\nlist_bを変更後:")
print("list_a:", list_a)
print("list_b:", list_b)
実行結果は次のようになります。
コピー直後:
list_a: [[1, 2], [3, 4]]
list_b: [[1, 2], [3, 4]]
list_bを変更後:
list_a: [[99, 2], [3, 4]]
list_b: [[99, 2], [3, 4]]
「えっ?list_b
を変えただけなのに、list_a
まで変わってる!」と驚きませんか?
これこそ浅いコピーの落とし穴です。
深いコピー(ディープコピー)とは?¶
次に、深いコピー(ディープコピー)を見てみましょう。
深いコピーは、外側の入れ物だけでなく、中にある要素(リストや辞書など)もすべて新しくコピーしてくれる方法です。
さきほどの例を深いコピーでやってみます。
import copy
list_a = [[1, 2], [3, 4]]
list_c = copy.deepcopy(list_a) # 深いコピー
print("コピー直後:")
print("list_a:", list_a)
print("list_c:", list_c)
list_c[0][0] = 99 # 内側のリストを変更
print("\nlist_cを変更後:")
print("list_a:", list_a)
print("list_c:", list_c)
結果はこうなります。
コピー直後:
list_a: [[1, 2], [3, 4]]
list_c: [[1, 2], [3, 4]]
list_cを変更後:
list_a: [[1, 2], [3, 4]]
list_c: [[99, 2], [3, 4]]
今度はlist_a
には影響が出ません。これが深いコピーの力です。
つまり中身ごとまるごと別物を作りたいときは、deepcopy を使う必要があるのです。
浅いコピーと深いコピーの比較¶
ここで一度、表で整理してみましょう。
コピー方法 | 外側のオブジェクト | 内側のオブジェクト | 結果の特徴 |
---|---|---|---|
浅いコピー (copy) | 新しく作られる | 参照を共有する | 入れ子の中身を変えると影響が出る |
深いコピー (deepcopy) | 新しく作られる | 中身もすべてコピー | 完全に独立した別物ができる |
この違いを理解していないと、「なんで勝手に値が変わったの?」という混乱に陥ってしまうのです。 両者の違いをしっかりと理解しておきましょう!
なぜPythonはこんな仕組みなのか?¶
「じゃあ、最初から全部深いコピーでいいんじゃないの?」
と感じるかもしれません。
しかし、深いコピーは便利な反面、コスト(処理時間とメモリ)が大きいというデメリットがあります。
- 浅いコピーは「外側だけ作り直す」ので高速で軽量
- 深いコピーは「中身を全部たどって複製する」ので処理が重くなります
エンジニア歴10年の実感としても、普段は浅いコピーで十分なケースが多いです。 深いコピーが必要なのは、データ構造が複雑で「絶対に独立して扱いたい」ときに限られます。
初心者がよくやるミス¶
私が新人の方のコードをレビューしていて特によく見るのは、次のようなケースです。
list_a = [[1, 2], [3, 4]]
list_b = list_a # コピーのつもり
list_b[0][0] = 99
print("list_a:", list_a)
print("list_b:", list_b)
一見コピーしているように見えますが、これは単なる代入です。
結果として、list_a
とlist_b
は同じオブジェクトを指してしまいます。
これが意図しないバグの温床になるのです。
実務での活用例¶
実際の開発現場では、データをコピーして別々に処理したい場面は意外と多くあります。 たとえば機械学習の前処理では、元データを保持しつつ、加工用のデータを作りたいケースが典型です。
私は以前、CSVで読み込んだ顧客データを分析用とレポート用に分ける処理を担当したことがあります。 そのときに浅いコピーを使ってしまい、データクリーニングの処理が元データに影響してしまうトラブルがありました。
「なんでレポートが壊れてるんだ?」と何時間も悩んだ末、原因は浅いコピーの罠でした。
それ以来、「複雑なデータを扱うときは必ずdeepcopy
を検討する」という習慣が身につきました。
まとめ¶
浅いコピーと深いコピーの違いを理解することは、Pythonを本当に使いこなす第一歩です。
- 浅いコピーは外側だけをコピーして、中身は共有する
- 深いコピーは中身までまるごとコピーする
- 実務では浅いコピーで十分な場合が多いが、複雑なデータでは深いコピーが必須
「なぜ勝手に変わるの?」という初心者の疑問は、ここに原因があるのです。 この記事を読んで、コピーの仕組みをしっかり理解しておけば、データ構造のトラブルで頭を抱える時間を大幅に減らせるはずです。
エンジニア歴10年の私から言わせてもらうと、この「コピーの違い」を早めに理解することが、Pythonスキルを一気に引き上げる近道だと思います。
あなたも次に同じ疑問を持つ人に出会ったら、自信を持って説明してあげてください!
ここまでお読みいただきありがとうございました!