JSONからイミュータブルなPython Dataclassの作成

JSONデータからfrozen(イミュータブル)なPython dataclassを生成する方法を学びます。不意の変更を防ぎ、setやdictキーで使用するためのハッシュ化を有効にします。

Dataclasses

詳細な説明

frozen=Trueによるイミュータブルなdataclass

dataclassに frozen=True を設定すると、構築後のすべてのフィールドが読み取り専用になります。フィールドを変更しようとすると FrozenInstanceError が発生します。

JSONの例

{
  "id": "usr_001",
  "name": "Alice",
  "created_at": "2024-01-15T08:00:00Z"
}

生成されるPython

from dataclasses import dataclass

@dataclass(frozen=True)
class User:
    id: str
    name: str
    created_at: str

frozen=Trueが提供するもの

  1. イミュータビリティuser.name = "Bob"FrozenInstanceError を発生させます。
  2. ハッシュ可能性 — frozenなdataclassは自動的に __hash__ メソッドを取得し、setやdictキーとして使用できます。
  3. スレッド安全性 — イミュータブルなオブジェクトはスレッド間で安全に共有できます。

frozenインスタンスの使用

user = User(id="usr_001", name="Alice", created_at="2024-01-15T08:00:00Z")

# setで使用
users = {user}

# dictキーとして使用
cache = {user: "some_value"}

# 変更を試みるとエラーが発生
user.name = "Bob"  # FrozenInstanceError!

変更されたコピーの作成

frozenインスタンスは変更できないため、dataclasses.replace() を使って一部のフィールドが変更された新しいインスタンスを作成します:

from dataclasses import replace

updated = replace(user, name="Bob")
# User(id='usr_001', name='Bob', created_at='2024-01-15T08:00:00Z')

frozen=True vs slots=True

Python 3.10+では両方を組み合わせることができます:

@dataclass(frozen=True, slots=True)
class User:
    id: str
    name: str

slots=True__dict__ の代わりに __slots__ を使用し、メモリ使用量を削減し属性アクセス速度を向上させます。frozen=True と組み合わせると、イミュータブルでメモリ効率の良い値オブジェクトが得られます。

frozenなDataclassを使うべき場面

  • 値オブジェクト — 作成後に変更されるべきでないID、座標、金額。
  • 設定 — 一度パースされ、あらゆる場所で使用され、変更されることのない設定。
  • キャッシュキー — dictキーやsetメンバーにはハッシュ可能性が必要です。
  • ドメインイベント — イベントは起こった事実を表すもので、イミュータブルなレコードであるべきです。

ユースケース

APIレスポンスオブジェクトをディクショナリキーとして使用するキャッシュレイヤーを構築しており、キャッシュの破損を防ぐためにハッシュ可能かつイミュータブルである必要がある場合に使用します。

試してみる — JSON to Python Converter

フルツールを開く