Python copy()とdeepcopy()の違い|浅いコピー・深いコピーを解説

Pythonでは、リストや辞書などのオブジェクトをコピーするときに、copy()やdeepcopy()を使用できます。

特に注意したいのが、copy()は「浅いコピー」、deepcopy()は「深いコピー」であるという違いです。

この記事では、Pythonのcopy()とdeepcopy()について、サンプルコードを使ってわかりやすく解説します。

Pythonのcopy()とdeepcopy()とは?

copy()とdeepcopy()は、Pythonのcopyモジュールで提供されているコピー用の関数です。

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

import copy

a = [1, 2, 3]

b = copy.copy(a)
c = copy.deepcopy(a)

copy.copy()は浅いコピーを作成し、copy.deepcopy()は深いコピーを作成します。

関数 意味
copy.copy() 浅いコピー
copy.deepcopy() 深いコピー

copy()は浅いコピー

copy.copy()は、元のオブジェクトを浅くコピーします。

例えば、リストをコピーしてみましょう。

import copy

a = [1, 2, 3]
b = copy.copy(a)

b[0] = 100

print(a)
print(b)

実行結果は次のようになります。

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

この場合、aとbは別のリストなので、bを変更してもaは変わりません。

copy()では入れ子になったオブジェクトに注意

copy()を使うときに注意したいのが、リストなどの中に別のリストが入っている場合です。

import copy

a = [[1, 2], [3, 4]]
b = copy.copy(a)

b[0][0] = 100

print(a)
print(b)

実行結果は次のようになります。

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

一見すると、bだけを変更したように見えます。

しかし、aの中身まで変更されています。

これは、copy()が外側のリストだけをコピーし、内側のリストまではコピーしないためです。

deepcopy()は深いコピー

deepcopy()は、入れ子になったオブジェクトも含めてコピーします。

import copy

a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)

b[0][0] = 100

print(a)
print(b)

実行結果は次のようになります。

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

今度は、bの中のリストを変更しても、aは変更されません。

これが、浅いコピーと深いコピーの大きな違いです。

copy()とdeepcopy()の違い

最も重要な違いを表にすると次のようになります。

copy() deepcopy()
コピーの種類 浅いコピー 深いコピー
外側のオブジェクト コピーする コピーする
入れ子のオブジェクト 基本的に共有される コピーされる
処理速度 比較的速い 比較的遅い

copy()とdeepcopy()の違いを確認する

オブジェクトが同じものを参照しているかどうかは、is演算子で確認できます。

import copy

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

b = copy.copy(a)
c = copy.deepcopy(a)

print(a is b)
print(a is c)

print(a[0] is b[0])
print(a[0] is c[0])

実行結果は次のようになります。

False
False
True
False

copy()では、外側のリストは別物になりますが、中に入っているリストは同じオブジェクトを参照しています。

一方、deepcopy()では、内側のリストも別のオブジェクトとしてコピーされます。

リストのcopy()メソッドとの違い

Pythonのリストには、copy()というメソッドもあります。

a = [1, 2, 3]
b = a.copy()

このlist.copy()は、copy.copy()と同じように浅いコピーを作成します。

例えば次のコードです。

a = [[1, 2], [3, 4]]
b = a.copy()

b[0][0] = 100

print(a)

実行結果は次のようになります。

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

入れ子になったリストまで完全に独立させたい場合は、deepcopy()を使用します。

import copy

a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)

b[0][0] = 100

print(a)

実行結果は次のようになります。

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

copy.copy()と代入は違う

コピーについて理解するときは、単純な代入との違いも重要です。

a = [1, 2, 3]
b = a

b[0] = 100

print(a)
print(b)

実行結果は次のようになります。

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

これは、b = aではリストそのものをコピーしていないためです。

aとbが同じリストを参照しています。

一方、copy.copy()を使うと外側のオブジェクトがコピーされます。

import copy

a = [1, 2, 3]
b = copy.copy(a)

b[0] = 100

print(a)
print(b)

実行結果は次のようになります。

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

スライスによるコピーとの違い

リストでは、スライスを使ってコピーする方法もあります。

a = [1, 2, 3]
b = a[:]

この方法も、基本的には浅いコピーです。

そのため、入れ子になったリストでは次のような問題が起こります。

a = [[1, 2], [3, 4]]
b = a[:]

b[0][0] = 100

print(a)

実行結果は次のようになります。

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

内側のリストまでコピーしたい場合は、deepcopy()を使用します。

deepcopy()を使う場面

deepcopy()は、元のデータとコピーしたデータを完全に独立させたい場合に便利です。

例えば、設定データの複製などに利用できます。

import copy

config = {
    "user": {
        "name": "Taro",
        "age": 20
    },
    "options": {
        "debug": True
    }
}

new_config = copy.deepcopy(config)

new_config["user"]["age"] = 30

print(config["user"]["age"])
print(new_config["user"]["age"])

実行結果は次のようになります。

20
30

元のconfigを変更せずに、コピーしたデータだけを変更できます。

deepcopy()を使えば常に良いわけではない

深いコピーは便利ですが、必要のない場面でdeepcopy()を使う必要はありません。

浅いコピーで十分な場合は、copy()やリストのcopy()メソッドなどを使ったほうが処理が軽くなります。

例えば、単純な1階層のリストであれば、次のようなコピーで十分です。

a = [1, 2, 3]
b = a.copy()

一方、複雑な入れ子構造を独立させたい場合には、deepcopy()が候補になります。

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

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

Pythonのcopy()とdeepcopy()まとめ

Pythonのcopy()とdeepcopy()の違いを理解するポイントは、「入れ子になったオブジェクトまでコピーするか」です。

  • copy.copy()は浅いコピー
  • copy.deepcopy()は深いコピー
  • 浅いコピーでは、入れ子のオブジェクトが共有されることがある
  • 深いコピーでは、入れ子のオブジェクトもコピーされる
  • リストのcopy()メソッドも浅いコピー
  • b = aはコピーではなく同じオブジェクトへの代入

特に、リストや辞書が入れ子になっている場合は、copy()とdeepcopy()の違いを意識することが重要です。

「コピーしたはずなのに元のデータまで変わってしまった」という問題は、浅いコピーとオブジェクト参照の仕組みを理解すると原因を把握しやすくなります。


あわせて読みたい

copy()は、Pythonのリスト(list)・辞書(dict)・集合(set)で使えるメソッドです。ほかの関数・メソッドとあわせて確認したい方は、次の一覧ページをご覧ください。

詳しくはPython copy()とは?リストや辞書をコピーする方法を初心者向けに解説で解説しています。

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

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