「このリストをもう一つ同じものとしてコピーしたい」
「a = b
でコピーしたつもりなのに、なぜか両方とも変わってしまう…」
そんな疑問を解決するために、今回はリストのコピー方法について、初心者の方でも理解できるように詳しく説明していきます。
まずは基本:「=」での代入はコピーではない

多くの初心者が陥りがちな間違いから見ていきましょう。
よくある間違い
# 間違ったコピー方法
original = [1, 2, 3, 4, 5]
copied = original # これはコピーではない!
print("元のリスト:", original) # [1, 2, 3, 4, 5]
print("コピー:", copied) # [1, 2, 3, 4, 5]
# copied を変更してみる
copied[0] = 100
print("変更後の元のリスト:", original) # [100, 2, 3, 4, 5] ← なぜか変わってしまう!
print("変更後のコピー:", copied) # [100, 2, 3, 4, 5]
なぜこうなるのか?
original = [1, 2, 3, 4, 5]
copied = original
# 同じオブジェクトを指しているか確認
print("同じオブジェクト?:", original is copied) # True
print("originalのID:", id(original))
print("copiedのID:", id(copied)) # 同じIDが表示される
pythonでは、リストはオブジェクトの参照(アドレス)で管理されています。
copied = original
は「copiedもoriginalと同じリストを指す」ことになります。
つまり、同じ箱を2つの名前で呼んでいるだけなのです。
図解:参照とコピーの違い
参照(=での代入):
original → [1, 2, 3, 4, 5] ← copied
同じリストを指している
コピー(この記事でやりたいこと):
original → [1, 2, 3, 4, 5]
copied → [1, 2, 3, 4, 5] ← 別のリスト
浅いコピー(シャローコピー)の方法
「浅いコピー」とは、リストの1階層目だけをコピーする方法です。
方法1:スライスを使う(最もシンプル)
original = [1, 2, 3, 4, 5]
copied = original[:] # 全体をスライス
# 変更して確認
copied[0] = 100
print("元のリスト:", original) # [1, 2, 3, 4, 5] ← 変更されない
print("コピー:", copied) # [100, 2, 3, 4, 5]
# 異なるオブジェクトか確認
print("異なるオブジェクト?:", original is not copied) # True
方法2:list()関数を使う
original = [1, 2, 3, 4, 5]
copied = list(original)
copied[1] = 200
print("元のリスト:", original) # [1, 2, 3, 4, 5] ← 変更されない
print("コピー:", copied) # [1, 200, 3, 4, 5]
方法3:copy()メソッドを使う
original = [1, 2, 3, 4, 5]
copied = original.copy()
copied[2] = 300
print("元のリスト:", original) # [1, 2, 3, 4, 5] ← 変更されない
print("コピー:", copied) # [1, 2, 300, 4, 5]
方法4:copyモジュールのcopy()を使う
import copy
original = [1, 2, 3, 4, 5]
copied = copy.copy(original)
copied[3] = 400
print("元のリスト:", original) # [1, 2, 3, 4, 5] ← 変更されない
print("コピー:", copied) # [1, 2, 3, 400, 5]
どの方法を使えば良い?
方法 | 特徴 | おすすめ度 |
---|---|---|
[:] | 最もシンプル、読みやすい | 高 |
list() | 明示的でわかりやすい | 中 |
.copy() | メソッドなので直感的 | 高 |
copy.copy() | importが必要だが統一感がある | 低 |
浅いコピーの限界:ネストされたリストの問題

浅いコピーには重要な制限があります。
リストの中にリストがある場合(ネストされたリスト)、内側のリストは共有されたままになってしまいます。
問題を実際に見てみる
# 2次元リスト(リストの中にリストがある)
original = [[1, 2], [3, 4], [5, 6]]
copied = original[:] # 浅いコピー
print("コピー前:")
print("元のリスト:", original) # [[1, 2], [3, 4], [5, 6]]
print("コピー:", copied) # [[1, 2], [3, 4], [5, 6]]
# 内側のリストを変更してみる
copied[0][0] = 100
print("変更後:")
print("元のリスト:", original) # [[100, 2], [3, 4], [5, 6]] ← なぜか変わってしまう!
print("コピー:", copied) # [[100, 2], [3, 4], [5, 6]]
なぜこうなるのか?
original = [[1, 2], [3, 4], [5, 6]]
copied = original[:]
# 外側のリストは別オブジェクト
print("外側のリストは別?:", original is not copied) # True
# しかし内側のリストは同じオブジェクト
print("内側のリスト[0]は同じ?:", original[0] is copied[0]) # True
print("内側のリスト[1]は同じ?:", original[1] is copied[1]) # True
図解:浅いコピーの構造
浅いコピーの場合:
original → [[1, 2], [3, 4], [5, 6]]
↑ ↑ ↑
copied → [ 同じ 同じ 同じ ] ← 外側は別だが内側は共有
深いコピー(ディープコピー)で完全にコピー
ネストされたリストを完全にコピーするには、「深いコピー」を使います。
copy.deepcopy()を使う
import copy
# 2次元リスト
original = [[1, 2], [3, 4], [5, 6]]
copied = copy.deepcopy(original) # 深いコピー
print("コピー前:")
print("元のリスト:", original) # [[1, 2], [3, 4], [5, 6]]
print("コピー:", copied) # [[1, 2], [3, 4], [5, 6]]
# 内側のリストを変更
copied[0][0] = 100
print("変更後:")
print("元のリスト:", original) # [[1, 2], [3, 4], [5, 6]] ← 変更されない!
print("コピー:", copied) # [[100, 2], [3, 4], [5, 6]]
# 内側のリストも別オブジェクトになっている
print("内側のリスト[0]は別?:", original[0] is not copied[0]) # True
より複雑な例
import copy
# 3次元リスト
original = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
# 浅いコピーでは不十分
shallow = original[:]
shallow[0][0][0] = 999
print("浅いコピー後の元のリスト:", original) # [[[999, 2], [3, 4]], [[5, 6], [7, 8]]]
# 元に戻す
original = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
# 深いコピーなら大丈夫
deep = copy.deepcopy(original)
deep[0][0][0] = 999
print("深いコピー後の元のリスト:", original) # [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
図解:深いコピーの構造
深いコピーの場合:
original → [[1, 2], [3, 4], [5, 6]]
copied → [[1, 2], [3, 4], [5, 6]] ← 全て別のオブジェクト
いろいろなデータ型でのコピー

辞書を含むリスト
import copy
original = [
{"name": "太郎", "scores": [85, 90]},
{"name": "花子", "scores": [88, 92]}
]
# 浅いコピーでは辞書の中身が共有される
shallow = original[:]
shallow[0]["scores"][0] = 100
print("浅いコピー後:", original[0]["scores"]) # [100, 90] ← 変更されてしまう
# 元に戻す
original[0]["scores"][0] = 85
# 深いコピーなら大丈夫
deep = copy.deepcopy(original)
deep[0]["scores"][0] = 100
print("深いコピー後:", original[0]["scores"]) # [85, 90] ← 変更されない
関数を含むリスト
import copy
def hello():
return "Hello!"
def goodbye():
return "Goodbye!"
original = [hello, goodbye, [1, 2, 3]]
# 関数はコピーされるが、参照は共有される
copied = copy.deepcopy(original)
print("関数は同じオブジェクト?:", original[0] is copied[0]) # True(関数は共有)
print("リストは別オブジェクト?:", original[2] is not copied[2]) # True(リストは別)
コピー方法の選び方
フローチャート形式で判断
def choose_copy_method(data):
"""適切なコピー方法を判断する"""
# 1. リストの中身をチェック
has_nested = any(isinstance(item, (list, dict)) for item in data)
if not has_nested:
print("推奨:浅いコピー ([:]、.copy()、list())")
return data[:]
else:
print("推奨:深いコピー (copy.deepcopy())")
import copy
return copy.deepcopy(data)
# テスト
simple_list = [1, 2, 3, "hello"]
nested_list = [1, [2, 3], {"key": "value"}]
print("シンプルなリスト:")
choose_copy_method(simple_list)
print("\nネストされたリスト:")
choose_copy_method(nested_list)
一覧表
状況 | おすすめの方法 | 理由 |
---|---|---|
数値・文字列のみのリスト | list[:] または .copy() | シンプルで高速 |
リストの中にリストがある | copy.deepcopy() | 内側も完全コピー |
辞書を含むリスト | copy.deepcopy() | 辞書の中身も完全コピー |
大量のデータ | 必要に応じて判断 | deepcopyは処理が重い |
パフォーマンスの比較

コピー方法によってパフォーマンスが異なります。
実際に測定してみる
import copy
import time
# テスト用データ
simple_data = list(range(10000))
nested_data = [[i, i+1] for i in range(1000)]
def measure_time(func, data, iterations=1000):
"""実行時間を測定する"""
start = time.time()
for _ in range(iterations):
func(data)
end = time.time()
return (end - start) / iterations
# 各コピー方法の速度測定
print("シンプルなリストでの速度比較:")
print(f"[:] : {measure_time(lambda x: x[:], simple_data):.6f}秒")
print(f".copy() : {measure_time(lambda x: x.copy(), simple_data):.6f}秒")
print(f"list() : {measure_time(lambda x: list(x), simple_data):.6f}秒")
print(f"copy.copy() : {measure_time(lambda x: copy.copy(x), simple_data):.6f}秒")
print("\nネストされたリストでの速度比較:")
print(f"[:] : {measure_time(lambda x: x[:], nested_data):.6f}秒")
print(f"copy.deepcopy(): {measure_time(lambda x: copy.deepcopy(x), nested_data):.6f}秒")
一般的な傾向
- 浅いコピー:
[:]
≈.copy()
<list()
<copy.copy()
- 深いコピー:
copy.deepcopy()
は浅いコピーより10倍以上遅い場合がある - メモリ使用量:深いコピーは元のデータと同じ分だけメモリを使用
実践的な使用例
設定データの管理
import copy
# デフォルト設定
default_config = {
"database": {"host": "localhost", "port": 5432},
"cache": {"enabled": True, "ttl": 300},
"features": ["login", "search", "upload"]
}
def create_user_config(user_overrides):
"""ユーザー固有の設定を作成"""
# 深いコピーでデフォルト設定をコピー
user_config = copy.deepcopy(default_config)
# ユーザーの設定で上書き
for key, value in user_overrides.items():
if key in user_config:
user_config[key].update(value)
return user_config
# 使用例
user1_overrides = {"database": {"host": "db1.example.com"}}
user2_overrides = {"cache": {"ttl": 600}}
user1_config = create_user_config(user1_overrides)
user2_config = create_user_config(user2_overrides)
print("デフォルト設定:", default_config["database"]["host"]) # localhost
print("ユーザー1設定:", user1_config["database"]["host"]) # db1.example.com
print("ユーザー2設定:", user2_config["database"]["host"]) # localhost
ゲームの状態管理
import copy
class GameState:
def __init__(self):
self.board = [[0 for _ in range(3)] for _ in range(3)]
self.current_player = 1
self.move_count = 0
def make_move(self, row, col):
"""手を打つ"""
if self.board[row][col] == 0:
self.board[row][col] = self.current_player
self.current_player = 3 - self.current_player # 1→2, 2→1
self.move_count += 1
return True
return False
def copy_state(self):
"""現在の状態のコピーを作成"""
return copy.deepcopy(self)
def display(self):
"""盤面を表示"""
for row in self.board:
print(row)
print(f"現在のプレイヤー: {self.current_player}")
print(f"手数: {self.move_count}")
# 使用例
game = GameState()
game.make_move(0, 0) # プレイヤー1が(0,0)に
game.make_move(1, 1) # プレイヤー2が(1,1)に
# 状態を保存
saved_state = game.copy_state()
# さらに手を進める
game.make_move(0, 1)
game.make_move(2, 2)
print("現在のゲーム状態:")
game.display()
print("\n保存された状態:")
saved_state.display()
データの変換処理
import copy
def safe_data_transformation(data, transform_func):
"""元データを保持しながらデータ変換を行う"""
# 元データのバックアップを作成
backup = copy.deepcopy(data)
try:
# データ変換を実行
result = transform_func(data)
return result, backup
except Exception as e:
print(f"変換エラー: {e}")
return backup, backup # エラー時は元データを返す
def risky_transformation(data):
"""リスクのあるデータ変換(例)"""
for item in data:
if "values" in item:
# 危険な操作:0で割る可能性
item["average"] = sum(item["values"]) / len(item["values"])
return data
# 使用例
original_data = [
{"name": "データ1", "values": [10, 20, 30]},
{"name": "データ2", "values": []}, # 空リスト(エラーの原因)
{"name": "データ3", "values": [5, 15, 25]}
]
result, backup = safe_data_transformation(original_data, risky_transformation)
print("元データは保護されています:")
print("バックアップの最初の項目:", backup[0])
よくある間違いと解決方法

間違い1:浅いコピーで十分だと思い込む
# 間違い
data = [{"users": ["Alice", "Bob"]}, {"users": ["Charlie"]}]
copied = data[:]
copied[0]["users"].append("David")
print(data[0]["users"]) # ['Alice', 'Bob', 'David'] ← 変更されてしまう
# 正しい
import copy
data = [{"users": ["Alice", "Bob"]}, {"users": ["Charlie"]}]
copied = copy.deepcopy(data)
copied[0]["users"].append("David")
print(data[0]["users"]) # ['Alice', 'Bob'] ← 変更されない
間違い2:copyモジュールのimportを忘れる
# 間違い
# data = copy.deepcopy(original) # NameError
# 正しい
import copy
data = copy.deepcopy(original)
間違い3:無駄に深いコピーを使う
# 無駄(シンプルなデータに深いコピー)
simple_data = [1, 2, 3, "hello", True]
copied = copy.deepcopy(simple_data) # 処理が重い
# 効率的
simple_data = [1, 2, 3, "hello", True]
copied = simple_data[:] # 軽快
よくある質問
- Qどの方法が一番速い?
- A
シンプルなリストなら
[:]
や.copy()
が最速です。ネストされたデータなら必要性を考慮してcopy.deepcopy()
を使いましょう。
- Q文字列や数値はコピーしなくても大丈夫?
- A
文字列や数値はイミュータブル(変更不可)なので、コピーを考える必要はありません。
a = "hello"
b = a
a += " world"
print(b) # "hello" ← 変更されない
- Q大きなデータをコピーするとメモリ不足になる?
- A
可能性があります。本当にコピーが必要か検討し、必要なら部分的なコピーやジェネレータの使用を検討しましょう。
- Qカスタムオブジェクトのコピーは?
- A
copy.deepcopy()
が使えますが、__copy__()
や__deepcopy__()
メソッドを定義してカスタマイズできます。import copy
class CustomObject:
def __init__(self, value):
self.value = value
def __deepcopy__(self, memo):
# カスタムコピー処理
return CustomObject(copy.deepcopy(self.value, memo))
まとめ
pythonの「リストのコピー」は、簡単そうでいて奥が深いテーマです。
おさらい:
- 単純代入(=):コピーではなく参照の共有
- 浅いコピー:
[:]
、.copy()
、list()
で1階層のみコピー - 深いコピー:
copy.deepcopy()
でネストされた構造も完全コピー - 使い分け:データ構造に応じて適切な方法を選択
コメント