Python辞書におけるカスタムオブジェクトの保存:参照と深いコピーの重要性

Python プログラミングにおいて、辞書は非常に強力なデータ構造であり、キーと値のペアを関連付け、効率的にデータを検索および操作することを可能にします。カスタムオブジェクトを辞書に格納しようとすると、通常、重要な概念である「Python におけるオブジェクト参照(参照渡し)」が問題となります。つまり、カスタムオブジェクトを辞書に入れる場合、辞書はオブジェクトへの参照を格納するだけであり、オブジェクトの完全なコピーを作成しているわけではありません。

カスタムオブジェクトの基本的な例

以下の簡単な Person クラスを想定してください:

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

# Person オブジェクトを作成
p1 = Person("Alice", 30)

# オブジェクトを辞書に保存
people_dict = {}
people_dict["alice"] = p1

この例では、people_dict 辞書が "alice" というキーを持つ項目を含み、その値は Person 型の p1 オブジェクトへの参照です。 p1 の属性を変更すると:

p1.age = 31

辞書からオブジェクトにアクセスするときに、その年齢も更新されていることに気づきます:

print(people_dict["alice"].age)  # 出力: 31

これは、辞書が Person オブジェクトの独立したコピーを保存するのではなく、同じメモリ位置への参照を保存しているためです。

深復元と浅復元の違い

ネストされたデータ構造やカスタムオブジェクトを扱う場合、このような参照の動作は予期せぬ結果を引き起こす可能性があります。例えば、カスタムオブジェクトが可変型の属性(リストや別のカスタムオブジェクトなど)を含む場合、そのようなオブジェクトを辞書に直接格納し、その変更を加えると、辞書から取得したオブジェクトも影響を受けます。

class Address:
    def __init__(self, street, city):
        self.street = street
        self.city = city

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

address = Address("Main St.", "Springfield")
p1 = Person("Bob", 40, address)
people_dict["bob"] = p1

# 原始アドレスオブジェクトを変更
address.city = "Shelbyville"

# 辞書内の人のアドレスも変更される
print(people_dict["bob"].address.city)  # 出力:Shelbyville

解決策:深復元

このような共有状態の問題を回避するためには、辞書がオブジェクトの完全なコピーを格納していることを確認する必要があります。つまり、参照ではなく、独立したコピーである必要があります。Python は copy モジュールにある deepcopy 関数を使用してこれを実現します。

import copy

# オブジェクトを深復元で格納
people_dict["bob_deepcopy"] = copy.deepcopy(p1)

# 原始アドレスオブジェクトを変更しても、深復元されたオブジェクトには影響しない
address.city = "Capital City"
print(people_dict["bob"].address.city)  # 出力:Capital City
print(people_dict["bob_deepcopy"].address.city)  # 出力:Capital City

要するに、Python で辞書を使用してカスタムオブジェクトを格納する場合は、デフォルトでオブジェクトの参照が格納されることに注意してください。独立した状態を維持する必要がある場合は、deepcopy を使用して深復元を行い、共有参照による予期せぬデータ変更を防ぐようにしてください。

Licensed under CC BY-NC-SA 4.0
最終更新 2025年06月02日 20:54
金融ITプログラマーのいじくり回しと日常のつぶやき
Hugo で構築されています。
テーマ StackJimmy によって設計されています。