dataclassses.asdict()
の引数 dict_factory
の使い方についてかんたんにまとめました。
dataclasses.asdict()
とは
dataclasses.asdict()
は dataclass を渡すとそれを dict
に変換して返してくれる関数です。
フィールドの値が dataclass の場合や、フィールドの値が dict
/ list
/ tuple
でその中に dataclass が含まれる場合は再帰的に変換を行ってくれます。
from dataclasses import dataclass, asdict
from datetime import date
@dataclass
class Member:
name: str
@dataclass
class Team:
name: str
members: list[Member]
created_at: date
t1 = Team(
name='Team A',
members=[Member(name='Taro'), Member('Hanko')],
created_at=date(2022, 12, 31),
)
# => Team(name='Team A', members=[Member(name='Taro'), Member(name='Hanko')], created_at=datetime.date(2022, 12, 31))
asdict(t1)
# => {'name': 'Team A', 'members': [{'name': 'Taro'}, {'name': 'Hanko'}], 'created_at': datetime.date(2022, 12, 31)}
引数 dict_factory
の使い方
dataclasses.asdict()
には dict_factory
という非必須の引数があります。
dict
化の処理を差し替えられる機能ですが、記事執筆時点で Python 公式ドキュメントに詳しい説明が載っていません。
dataclasses.asdict()
のコードを見るとわかるのですが、 dict_factory
には「引数を 1 つ受け取り dict
を返す関数」である必要があります。
引数の型は list[tuple[str, typing.Any]]
です。
list
内の各 tuple
は長さが 2 でフィールドの名前と値を格納しています。
タイプヒント付きの関数を書くと次のようになります。
from typing import Any
def dict_factory(items: list[tuple[str, Any]]) -> dict[str, Any]:
...
たとえば、上の Team
クラスの場合は次のような関数を渡せば OK です。
def team_dict_factory(items: list[tuple[str, Any]]) -> dict[str, Any]:
"""`Team` 用の `dict_factory`"""
adict = {}
for key, value in items:
if isinstance(value, date):
value = value.strftime('%Y-%m-%d')
adict[key] = value
return adict
d2 = asdict(t1, dict_factory=team_dict_factory)
# => {'name': 'Team A', 'members': [{'name': 'Taro'}, {'name': 'Hanko'}], 'created_at': '2022-12-31'}