リストをコピーしたつもりなのに、コピー元の値まで変わってしまった……。Pythonを学び始めた方が必ずと言っていいほど経験する、あの不思議な現象です。
この記事では、代入・浅いコピー・深いコピーの違いを、図解のかわりに実行結果で順番に確認していきます。仕組みが分かれば、もう怖くありません。
代入はコピーではない
a = [1, 2, 3]
b = a # 代入
b.append(4)
print(a) # [1, 2, 3, 4]
print(b) # [1, 2, 3, 4]
print(a is b) # Trueb = a は、リストの中身をコピーしているのではなく、同じリストに別の名前を付けているだけです。そのため b を変更すると a も変わります。リストの基本操作はリスト内包表記の記事などもあわせて確認してみてください。
浅いコピー(shallow copy)
新しいリストを作りたいときは、copy() メソッドやスライスを使います。スライスについてはスライスの使い方で詳しく解説しています。
a = [1, 2, 3]
b = a.copy() # a[:] や list(a) でも同じ
b.append(4)
print(a) # [1, 2, 3]
print(b) # [1, 2, 3, 4]
print(a is b) # Falseこれで a と b は別のリストになりました。ただし、これで安心するのはまだ早いです。
浅いコピーの落とし穴:ネストしたリスト
a = [[1, 2], [3, 4]]
b = a.copy()
b[0].append(99)
print(a) # [[1, 2, 99], [3, 4]]
print(b) # [[1, 2, 99], [3, 4]]浅いコピーでは、外側のリストだけが新しくなり、中のリスト([1, 2] など)は同じものを共有します。そのため、中身を変更すると両方に反映されてしまいます。
深いコピー(deep copy)で解決
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[0].append(99)
print(a) # [[1, 2], [3, 4]]
print(b) # [[1, 2, 99], [3, 4]]copy.deepcopy を使うと、中のリストまで再帰的にコピーされるので、完全に独立したデータになります。
こうした「なぜそうなるのか」という仕組みの理解は、独学だと時間がかかりやすい部分です。効率よく基礎を固めたい方は、プログラミングスクールの選び方と比較軸も参考にしてください。
辞書・集合のコピー
import copy
d = {"name": "田中", "scores": [80, 90]}
shallow = d.copy()
deep = copy.deepcopy(d)
d["scores"].append(100)
print(shallow["scores"]) # [80, 90, 100]
print(deep["scores"]) # [80, 90]辞書も同じで、copy() は浅いコピーです。値にリストや辞書が入っているなら deepcopy を使いましょう。辞書の基本は辞書(dict)の使い方をご覧ください。
関数の引数でも起こる
def add_item(items):
items.append("追加")
my_list = ["A", "B"]
add_item(my_list)
print(my_list) # ['A', 'B', '追加']関数にリストを渡すと、コピーではなく同じリストが渡されます。元のデータを変えたくないときは、関数の中で items.copy() してから使いましょう。
どれを使うべき?
- 代入(=):同じものに別名を付けるだけ
- copy() / スライス:中身が数値や文字列だけのときに
- copy.deepcopy():リストや辞書がネストしているとき
もっと効率的にPythonを学びたい人へ
参照とコピーの違いは、バグの原因になりやすい重要ポイントです。「なんとなく動くけれど理解があいまい」という方は、マンツーマンで基礎を固められるスクールも検討してみてください。
Winスクールの「Python Winner」は、マンツーマン指導で未経験からPythonを学べ、転職サポートや無料カウンセリングにも対応しています。まずは無料カウンセリングで、自分に合うか確かめてみてはいかがでしょうか。
※本記事には広告(アフィリエイトリンク)を含みます。
Pythonプログラミング|マンツーマンレッスンで初めてをプロに育てる【Python Winner】![]()
他のスクールとも比べたい方は、スクールの比較記事もあわせてご覧ください。資格取得を目指す方はPython3エンジニア認定基礎試験の参考書比較も参考になります。
まとめ
- b = a は同じオブジェクトに名前を付けるだけで、コピーではない
- copy() やスライスは浅いコピーで、外側だけが新しくなる
- ネストしたデータは浅いコピーだと中身を共有してしまう
- 完全に独立させたいときは copy.deepcopy を使う
- 関数に渡したリストは元のリストが変更される点に注意
まずは a is b の結果を確かめながら、手元でいろいろ試してみましょう。
ナノトイラボ 
