Search code examples
pythonpython-dataclasses

nested dataclasses to dict


I have a lot of dataclasses that are nested using post_init

from dataclasses import dataclass
from typing import List
from typing import Optional
from typing import Union


@dataclass
class MyClass:
    signed_key: str
    signature: str


@dataclass
class Message:
    signature: str
    my_class: Union[MyClass, dict]
    protocol_version: str
    signed_message: str

    def __post_init__(self):
        self.my_class = MyClass(**self.my_class)

It work nice but the problem with this is that if i want to convert Message to dict like so:

#create a message object before
print(message.__dict__)

The output i get:

{'signature': 'test', 'my_class': Myclass(signed_key='test', signature='test'), 'protocol_version': 'test', 'signed_message': 'test'}

What i want (nested dict):

{'signature': 'test', 'my_class': {'signed_key': 'test', 'signature': 'test'}, 'protocol_version': 'test', 'signed_message': 'test'}

I could have use libs like attrs or pydantic but the goal here is to only use pure python


Solution

  • You can use dataclasses.asdict(Note that this is a module level function and not bound to any dataclass instance) and it's designed exactly for this purpose.

    dataclasses.asdict(instance, *, dict_factory=dict)

    Converts the dataclass instance to a dict (by using the factory function dict_factory). Each dataclass is converted to a dict of its fields, as name: value pairs. dataclasses, dicts, lists, and tuples are recursed into.

    Example,

    >>> from dataclasses import dataclass, asdict
    >>>
    >>> @dataclass
    ... class Message:
    ...     message: str
    ...
    >>>
    >>> @dataclass
    ... class Chat:
    ...     messages: list[Message]
    ...
    >>> chat = Chat([Message('Hi'), Message("Hello")])
    >>> chat.__dict__
    {'messages': [Message(message='Hi'), Message(message='Hello')]}
    >>> asdict(chat)
    {'messages': [{'message': 'Hi'}, {'message': 'Hello'}]}