【初心者向け】Python辞書のコピー方法まとめ

python

Pythonの辞書(dict)を使っているとき、「元のデータを変更せずに、コピーして使いたい!」という場面はよくあります。

でも、「=で代入したら両方が変わってしまった!」「どれが正しいコピー方法かわからない…」

そんな悩みを持つ方向けに、この記事では辞書をコピーする4つの方法と、それぞれの違い・注意点を丁寧に説明していきます。

スポンサーリンク

間違いやすい代入(コピーにならない!)

よくある間違い

original = {"name": "田中", "age": 25}
copy_data = original  # これは間違い!
copy_data["age"] = 30

print(original["age"])  # 結果: 30 😱

何が起きた?

  • copy_data = originalでは、コピーが作られません
  • copy_dataoriginal同じ辞書を指しています
  • どちらを変更しても、両方に影響します

図で理解:

original ──┐
           ├── {"name": "田中", "age": 25}
copy_data ─┘

まとめ

  • =は「コピー」ではなく「参照の共有」
  • 元のデータを守りたいときは使えません

正しいコピー方法①:dict.copy()

基本的な使い方

original = {"name": "田中", "age": 25}
copy_data = original.copy()  # 正しいコピー
copy_data["age"] = 30

print(original["age"])  # 結果: 25 🎉
print(copy_data["age"])  # 結果: 30

説明:

  • copy()メソッドは浅いコピー(shallow copy)を作ります
  • 辞書の中身が単純な値(文字列、数値など)なら、これで十分です

図で理解:

original  ── {"name": "田中", "age": 25}
copy_data ── {"name": "田中", "age": 30}

実用例:

# 設定の初期値をベースに、ユーザー設定を作る
default_settings = {"theme": "light", "font_size": 12, "auto_save": True}
user_settings = default_settings.copy()
user_settings["theme"] = "dark"

print(default_settings)  # 結果: {"theme": "light", "font_size": 12, "auto_save": True}
print(user_settings)     # 結果: {"theme": "dark", "font_size": 12, "auto_save": True}

正しいコピー方法②:dict()コンストラクタ

使い方

original = {"name": "田中", "age": 25}
copy_data = dict(original)  # copy()と同じ効果
copy_data["age"] = 30

print(original["age"])  # 結果: 25

説明:

  • dict()を使っても、copy()と同じ浅いコピーができます
  • どちらを使うかは好みの問題です

使い分けの例:

# 書き方1: copy()メソッド
copy1 = original.copy()

# 書き方2: dict()コンストラクタ
copy2 = dict(original)

# 書き方3: 辞書リテラル(Python 3.5以降)
copy3 = {**original}

正しいコピー方法③:copyモジュール(浅いコピー)

使い方

import copy

original = {"name": "田中", "age": 25}
shallow_copy = copy.copy(original)
shallow_copy["age"] = 30

print(original["age"])  # 結果: 25

説明:

  • copy.copy()は、辞書以外のデータ型でも使える汎用的な方法です
  • 辞書の場合はdict.copy()と同じ動作をします

どんなときに使う?

import copy

# 辞書だけでなく、リストやカスタムクラスでも使える
my_list = [1, 2, 3]
copied_list = copy.copy(my_list)

class Person:
    def __init__(self, name):
        self.name = name

person = Person("田中")
copied_person = copy.copy(person)

正しいコピー方法④:copy.deepcopy(深いコピー)

なぜ深いコピーが必要?

まず、浅いコピーの問題点を見てみましょう:

# 辞書の中に辞書がある(ネスト構造)
original = {
    "user": {"name": "田中", "age": 25},
    "settings": {"theme": "light"}
}

# 浅いコピーでは内側の辞書は共有される
shallow_copy = original.copy()
shallow_copy["user"]["age"] = 30  # 内側の辞書を変更

print(original["user"]["age"])  # 結果: 30 😱(元も変わってしまう)

なぜこうなる? 浅いコピーでは、外側の辞書はコピーされますが、内側の辞書は同じものを共有してしまいます。

図で理解:

original      ── {"user": ──┐, "settings": ──┐}
                           │                 │
shallow_copy ── {"user": ──┘, "settings": ──┘}
                           │                 │
                    {"name": "田中",    {"theme": "light"}
                     "age": 30}

深いコピーの使い方

import copy

original = {
    "user": {"name": "田中", "age": 25},
    "settings": {"theme": "light"}
}

# 深いコピーで完全に別のコピーを作る
deep_copy = copy.deepcopy(original)
deep_copy["user"]["age"] = 30

print(original["user"]["age"])  # 結果: 25 🎉(元は変わらない)
print(deep_copy["user"]["age"])  # 結果: 30

図で理解:

original  ── {"user": ── {"name": "田中", "age": 25}, "settings": ── {"theme": "light"}}
deep_copy ── {"user": ── {"name": "田中", "age": 30}, "settings": ── {"theme": "light"}}

実用例:

# ゲームの設定をプレイヤーごとにカスタマイズ
default_config = {
    "player": {"name": "", "level": 1},
    "graphics": {"resolution": "1920x1080", "quality": "high"},
    "controls": {"jump": "space", "move": "wasd"}
}

# プレイヤー1の設定
player1_config = copy.deepcopy(default_config)
player1_config["player"]["name"] = "太郎"
player1_config["graphics"]["quality"] = "low"

# プレイヤー2の設定
player2_config = copy.deepcopy(default_config)
player2_config["player"]["name"] = "花子"

# 元の設定は変わらない
print(default_config["player"]["name"])  # 結果: ""

コピー方法の比較表

方法コピーの深さ特徴ネスト構造への対応使う場面
=(代入)なしコピーではない、参照共有×使わない
.copy()浅いシンプル、辞書専用×単純な辞書
dict()浅い同上、書き方が違う×単純な辞書
copy.copy()浅い辞書以外でも使える×汎用的に使いたい
copy.deepcopy()深い完全に独立したコピーネスト構造がある

よくある注意点とトラブル対策

注意点1:浅いコピーではネストされたオブジェクトは共有される

original = {
    "fruits": ["apple", "banana"],
    "user": {"name": "田中"}
}

# 浅いコピーの問題
shallow_copy = original.copy()
shallow_copy["fruits"].append("orange")  # リストに要素追加
shallow_copy["user"]["name"] = "佐藤"    # 辞書の値変更

print(original["fruits"])    # 結果: ["apple", "banana", "orange"] 😱
print(original["user"]["name"])  # 結果: "佐藤" 😱

対策:

# 深いコピーを使う
deep_copy = copy.deepcopy(original)
deep_copy["fruits"].append("orange")
deep_copy["user"]["name"] = "佐藤"

print(original["fruits"])    # 結果: ["apple", "banana"] 🎉
print(original["user"]["name"])  # 結果: "田中" 🎉

注意点2:copy.deepcopy()は重い処理

import time
import copy

# 大きな辞書の例
large_dict = {}
for i in range(10000):
    large_dict[f"key_{i}"] = {"data": list(range(100))}

# 時間を測定
start_time = time.time()
deep_copy = copy.deepcopy(large_dict)
end_time = time.time()

print(f"深いコピーにかかった時間: {end_time - start_time:.3f}秒")

対策:

  • 本当に必要な場合のみdeepcopy()を使う
  • データ構造を工夫して、ネストを浅くする

注意点3:JSONを使った簡易的な深いコピー

import json

original = {
    "user": {"name": "田中", "age": 25},
    "settings": {"theme": "light"}
}

# JSONを使った深いコピー
json_copy = json.loads(json.dumps(original))
json_copy["user"]["age"] = 30

print(original["user"]["age"])  # 結果: 25 🎉

制限事項:

  • JSONで表現できるデータのみ(文字列、数値、リスト、辞書、真偽値、None)
  • 関数やカスタムクラスのインスタンスは使えない
  • 日付オブジェクトなども使えない

使える場面:

# これはOK
simple_data = {
    "name": "田中",
    "age": 25,
    "hobbies": ["読書", "映画"],
    "settings": {"notifications": True}
}

# これはエラーになる
import datetime
complex_data = {
    "name": "田中",
    "created": datetime.datetime.now(),  # 日付オブジェクト
    "process": lambda x: x * 2           # 関数
}

どの方法を選ぶべき?フローチャート

辞書をコピーしたい
    ↓
辞書の中にリストや辞書が含まれている?
    ↓                    ↓
   はい                 いいえ
    ↓                    ↓
copy.deepcopy()      dict.copy()
を使う              を使う

実践的な使用例

例1:設定ファイルの管理

import copy

# デフォルト設定
default_config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "timeout": 30
    },
    "logging": {
        "level": "INFO",
        "file": "app.log"
    }
}

# 本番環境用の設定
production_config = copy.deepcopy(default_config)
production_config["database"]["host"] = "prod-server.com"
production_config["logging"]["level"] = "ERROR"

# テスト環境用の設定
test_config = copy.deepcopy(default_config)
test_config["database"]["host"] = "test-server.com"
test_config["logging"]["level"] = "DEBUG"

# デフォルト設定は変わらない
print(default_config["database"]["host"])  # 結果: "localhost"

例2:ユーザープロフィールの管理

import copy

# ユーザープロフィールのテンプレート
profile_template = {
    "personal": {
        "name": "",
        "age": 0,
        "email": ""
    },
    "preferences": {
        "language": "ja",
        "timezone": "Asia/Tokyo",
        "notifications": True
    },
    "history": []
}

# 新しいユーザーのプロフィールを作成
def create_user_profile(name, age, email):
    profile = copy.deepcopy(profile_template)
    profile["personal"]["name"] = name
    profile["personal"]["age"] = age
    profile["personal"]["email"] = email
    return profile

# 使用例
user1 = create_user_profile("田中太郎", 25, "tanaka@example.com")
user2 = create_user_profile("佐藤花子", 30, "sato@example.com")

# 個別にカスタマイズ
user1["preferences"]["language"] = "en"
user2["history"].append("ログイン")

# テンプレートと他のユーザーは影響を受けない
print(profile_template["preferences"]["language"])  # 結果: "ja"
print(user2["preferences"]["language"])             # 結果: "ja"

まとめ

シーンおすすめの方法理由
単純な辞書のコピー.copy() または dict()高速で簡単
ネスト構造がある辞書copy.deepcopy()完全に独立したコピー
軽量で一時的なコピーjson.loads(json.dumps())シンプルなデータのみ
絶対に避ける=(代入)コピーにならない

コメント

タイトルとURLをコピーしました