環境
python3.10
web api のレスポンスを dataclass にマッピングしたい
web api のレスポンスがこんな感じだった場合
response = {
'user_id': 'xxxx@example.com',
'purchase_histories': [
{
'name': '鉛筆',
'price': 120
},
{
'name': '消しゴム',
'price': 150
}
]
}
次のような dataclass を2つ用意する。
from dataclasses import dataclass
@dataclass
class PurchaseHistory:
name: str
price: int
@dataclass
class Person:
user_id: str
purchase_histories: list[PurchaseHistory]
で、こんな感じで初期化する。
histories = [PurchaseHistory(name=history['name'], price=history['price']) for history in response['purchase_histories']]
p = Person(user_id=response['user_id'], purchase_histories=histories)
履歴の部分を取り出して先に PurchaseHistory の list を作って Person のコンストラクタに入れるって、使う側からすると使いにくい。
InitVar を使う
先ほどの Person クラスを以下のように変える。
@dataclass
class Person:
user_id: str
purchase_histories: list[PurchaseHistory] = field(init=False)
histories: InitVar[list[dict]]
def __post_init__(self, histories: dict):
self.purchase_histories = [PurchaseHistory(name=history['name'], price=history['price']) for history in histories]
そうすると、呼び出し側はこんな感じで呼び出せる。
p = Person(user_id=response['user_id'], histories=response['purchase_histories'])
InitVar を使うと、コンストラクタと、 post_init メソッド内でしか使えない変数が使えるようになって、それを使って本来必要な purchase_histories を初期化できる。