一覧へ

可変オブジェクトの参照伝播 — コピーの落とし穴

Pythonでリストをコピーしたのに、元のリストが変更される理由、シャローコピーとディープコピーの違い、copyモジュールの使い方を学びます。

中級
|
10
|
検証済み (2026-07)
可変オブジェクト参照伝播シャローコピーディープコピーmutableリストコピー
進捗0/18 (0%)

可変オブジェクトの参照伝播 — コピーの落とし穴

このトピックを終えると

Pythonでリストを = でコピーした場合に、なぜ元のオブジェクトが変更されるのかを理解し、浅いコピーと深いコピーを区別して使用できるようになります。


コピーしたのに元のオブジェクトが変更される

python
original = [1, 2, 3]
copy = original
copy.append(4)
print(original) # [1, 2, 3, 4] — 元のオブジェクトも変更される!

copy = originalコピーではありません。 同じリストに別の名前を付けただけです。originalcopy は、同じリストを指す 2 つの名前です。

前のトピック(変数とメモリ)で、変数は「値を格納する箱」ではなく、「オブジェクトを指す矢印」であると学びました。それが実際に問題を引き起こす瞬間が、まさにここです。

python
print(id(original)) # 4392957504
print(id(copy)) # 4392957504 — 同じアドレス!

本物のコピー — スライス

python
original = [1, 2, 3]
copy = original[:] # 完全なスライス = 新しいリスト
copy.append(4)
print(original) # [1, 2, 3] — 元のオブジェクトは変更されない
print(copy) # [1, 2, 3, 4]

[:] は、リストの最初から最後までをスライスして 新しいリストを作成します。 list(original)original.copy() も同じ効果があります。


浅いコピーの限界

python
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]
shallow[0][0] = 99
print(matrix) # [[99, 2], [3, 4]] — 元のオブジェクトも変更される!

スライスは 1 段階だけコピーします。 matrix[:] は、外側のリストを新しく作成しますが、内側の [1, 2][3, 4]同じオブジェクトを共有します。 内側を修正すると、元のオブジェクトにも反映されます。

これが 浅いコピー (shallow copy) です。


深いコピー — copy.deepcopy

python
import copy
matrix = [[1, 2], [3, 4]]
deep = copy.deepcopy(matrix)
deep[0][0] = 99
print(matrix) # [[1, 2], [3, 4]] — 元のオブジェクトは変更されない

deepcopy は、内部オブジェクトまですべて再帰的にコピーします。完全に独立した新しいオブジェクトです。


いつ何を使いましょうか

状況方法理由
1 次元のリストlst[:] または lst.copy()高速かつ簡単
2 次元以上のネストされたリストcopy.deepcopy(lst)内部リストまで独立させる必要がある
辞書dict.copy() または {**d}1 段階の浅いコピー
ネストされた辞書copy.deepcopy(d)内部の辞書/リストまで独立させる
実際にコピーする必要がない場合= のまま意図的に同じオブジェクトを共有する場合

関数引数でも同じ

python
def add_item(lst, item):
lst.append(item)
return lst
my_list = [1, 2, 3]
result = add_item(my_list, 4)
print(my_list) # [1, 2, 3, 4] — 元のオブジェクトが変更される

関数にリストを渡すと、参照が渡されます。 関数内で .append() を行うと、元のオブジェクトも変更されます。 元のオブジェクトを保持するには、関数内で最初にコピーしてください。

python
def add_item_safe(lst, item):
new_lst = lst[:]
new_lst.append(item)
return new_lst

可変 vs 不変のまとめ

可変/不変= コピー後、修正した場合の元のオブジェクトへの影響
intfloatstrtuple不変影響なし (いずれにせよ新しいオブジェクトが作成される)
listdictset可変元のオブジェクトも変更される

不変オブジェクトでは、この問題は発生しません。 文字列を「修正」すると、常に新しい文字列が作成されるためです。 可変オブジェクトでのみ注意が必要です。


重要なポイント

= はコピーではなく、同じオブジェクトに別の名前を付けることです。 1 次元は [:] または .copy()、ネストされた構造は copy.deepcopy() を使用します。 関数に可変オブジェクトを渡すと、元のオブジェクトが変更される可能性があります — 意図しない場合は、最初にコピーしてください。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...