🗂
【Python】dataclassをネストされたdictから構築する【dataclass】
先にソリューション
@dataclass
class RecursiveDataclass:
pass
@classmethod
def from_dict(cls, src: dict) -> RecursiveDataclass:
kwargs = dict()
field_dict: dict[str, Field] = {field.name: field for field in fields(cls)}
field_type_dict: dict[str, type] = get_type_hints(cls)
for src_key, src_value in src.items():
assert src_key in field_dict, "Invalid Data Structure"
field = field_dict[src_key]
field_type = field_type_dict[field.name]
if issubclass(field_type, RecursiveDataclass):
kwargs[src_key] = field_type.from_dict(src_value)
else:
kwargs[src_key] = src_value
return cls(**kwargs)
@dataclass
class Parent(RecursiveDataclass):
field_a: str
field_b: Child
@dataclass
class Child(RecursiveDataclass):
field_c: int
field_d: str
arg_dict = {"field_a": "hoge", "field_b": {"field_c": 3, "field_d": "fuga"}}
parent = Parent.from_dict(arg_dict)
print(parent) # Parent(field_a='hoge', field_b=Child(field_c=3, field_d='fuga'))
dataclassはasdictで再帰的にdictにできるけど、dictから再帰的にdataclassを構築することはできない
dictからdataclassを使うにはkwargsの展開YourClass(**dict)
が使われるけど、再帰的に展開はしてくれなずただ単にdictになる。
@dataclass
class Parent:
field_a: str
field_b: Child
@dataclass
class Child:
field_c: int
field_d: str
arg_dict = {"field_a": "hoge", "field_b": {"field_c": 3, "field_d": "fuga"}}
parent = Parent(**arg_dict)
# field_bがChildクラスのインスタンスではなく単なるdictとなってしまう。
print(parent) # Parent(field_a='hoge', field_b={'field_c': 3, 'field_d': 'fuga'})
Full Code
from __future__ import annotations
from dataclasses import Field, dataclass, fields
from typing import get_type_hints
@dataclass
class RecursiveDataclass:
pass
@classmethod
def from_dict(cls, src: dict) -> RecursiveDataclass:
kwargs = dict()
field_dict: dict[str, Field] = {field.name: field for field in fields(cls)}
field_type_dict: dict[str, type] = get_type_hints(cls)
for src_key, src_value in src.items():
assert src_key in field_dict, "Invalid Data Structure"
field = field_dict[src_key]
field_type = field_type_dict[field.name]
if issubclass(field_type, RecursiveDataclass):
kwargs[src_key] = field_type.from_dict(src_value)
else:
kwargs[src_key] = src_value
return cls(**kwargs)
@dataclass
class Parent(RecursiveDataclass):
field_a: str
field_b: Child
@dataclass(RecursiveDataclass)
class Child:
field_c: int
field_d: str
arg_dict = {"field_a": "hoge", "field_b": {"field_c": 3, "field_d": "fuga"}}
parent = Parent.from_dict(arg_dict)
Discussion