copy
モジュールによるオブジェクトの複製方法を整理しました。
特に copy()
(浅いコピー)と deepcopy()
(完全コピー)の違いは、試験でも実務でも重要です。
なぜコピーが必要?
Python ではオブジェクトを代入すると「同じものを共有」することになります。
a = [1, 2, 3]
b = a
b[0] = 999
print(a) # [999, 2, 3] ← aも変わってしまう!
-
b = a
は「aをコピー」しているのではなく、「aへの参照を代入」しています - そのため
b
を変更するとa
も変わってしまいます
このようなケースで「コピーして別物として扱いたい」ときに、copy
モジュールが登場します。
copy.copy():浅いコピー(shallow copy)
浅いコピーでは、最上位のオブジェクトだけがコピーされます。
その中にリストや辞書などの入れ子構造があると、それらは共有されます。
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
shallow[0][0] = 999
print(original) # [[999, 2], [3, 4]]
-
original
とshallow
は別のリストオブジェクト - ただし、中のリスト(
[1, 2]
など)は同じものを指している
🔍 「じゃあ代入と変わらなくない?」と感じた方へ
実はこの疑問、とても本質的です。
中の要素が共有されるせいで「代入と変わらないのでは?」と思えてしまうのは自然なことです。
でも、外側のリスト自体は完全に別物であるという点が決定的に異なります。
import copy
a = [[1, 2], [3, 4]]
b = a # 代入
c = copy.copy(a) # 浅いコピー
b.append([5, 6])
c.append([7, 8])
print("a:", a) # [[1, 2], [3, 4], [5, 6]]
print("b:", b) # [[1, 2], [3, 4], [5, 6]]
print("c:", c) # [[1, 2], [3, 4], [7, 8]]
-
b
はa
に完全に追従している(代入) -
c
はa
のコピーなので、別のリストとして.append()
ができる
つまり、浅いコピーは:
- 外側の構造をコピーして独立させたいとき
- でも内側のデータ(ネスト)は変更しない前提
という場面で有効です。
copy.deepcopy():完全コピー(deep copy)
deepcopy() は、中の要素も含めてすべて再帰的にコピーします。
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 999
print(original) # [[1, 2], [3, 4]] ← 元のデータは影響を受けない
-
deep
の中にあるリストもoriginal
とは完全に別物になります - そのため、変更が元に影響しません
copy.copy() と代入と deepcopy の違いまとめ
操作内容 | リスト本体 | 内部要素(入れ子) |
---|---|---|
= (代入) |
同じ | 同じ |
copy.copy() |
別 | 同じ |
copy.deepcopy() |
別 | 別 |
いつ使い分ける?
-
入れ子構造がない場合 →
copy.copy()
でも OK -
外側だけ分離したい(中身は共有OK) →
copy.copy()
-
元データと完全に独立させたい →
copy.deepcopy()
おわりに
今回は copy
モジュールによるオブジェクトのコピー方法をまとめました。
特に copy()
は見た目はコピーっぽく見えても中身が共有されてしまう点に注意が必要です。
最初は「なんで元まで変わるの?」と混乱しましたが、copy()
は表面だけをコピーし、deepcopy()
は中身まで完全にコピーしてくれると覚えると分かりやすかったです。
また、「浅いコピーって代入と何が違うの?」と疑問に思ったのですが、外側のオブジェクトだけが独立していて、中身は共有されるという仕組みを理解することで納得感が得られました。
この違いを意識することで、どんな場面で copy()
を使うべきかがだんだん見えてきたように思います。