[issue34409] Add a way to customize iteration over fields in asdict() for the nested dataclasses
mkurnikov added the comment: Cleanest thing I could think of is: 1. Extract dataclass_to_dict function from _asdict_inner as: def dataclass_asdict(obj, dict_factory): result = [] for f in fields(obj): value = _asdict_inner(getattr(obj, f.name), dict_factory) result.append((f.name, value)) return dict_factory(result) 2. Add "asdict" parameter to the dataclass decorator (with default value of dataclass_to_dict function) @dataclass(asdict=specific_dcls_dict_factory) class MyDataclass: pass 3. Change check to def _asdict_inner(obj, dict_factory): if _is_dataclass_instance(obj): return getattr(obj, _PARAMS).asdict(obj) # ... other code Other solution could be to add parameter "directly_serializable"(smth like that), add check for this parameter def _asdict_inner(obj, dict_factory): if _is_dataclass_instance(obj): if getattr(obj, _PARAMS).directly_serializable: return dict_factory(obj) # ... other code and force user to process everything in dict_factory function. -- ___ Python tracker <https://bugs.python.org/issue34409> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue34409] Add a way to customize iteration over fields in asdict() for the nested dataclasses
mkurnikov added the comment: from pprint import pprint from typing import List, Any, Dict import dataclasses from dataclasses import field def service_interface_dict_factory(obj: Any) -> Dict[str, Any]: print(type(obj)) # <- type(obj) here is a list, but there's no way to understand whether it's a ServiceInterface or # InputVar except for looking for the presence of certain keys which is not very convenient return dict(obj) @dataclasses.dataclass class InputVar(object): name: str required: bool = False options: Dict[str, Any] = field(default_factory=dict) @dataclasses.dataclass class ServiceInterface(object): input: List[InputVar] = field(default_factory=list) if __name__ == '__main__': inputvar_inst = InputVar(name='myinput', required=False, options={'default': 'mytext'}) interface = ServiceInterface(input=[inputvar_inst]) outdict = dataclasses.asdict(interface, dict_factory=service_interface_dict_factory) print('outdict', end=' ') pprint(outdict) # prints: # outdict {'input': [{'name': 'myinput', # 'options': {'default': 'mytext'}, # 'required': False}]} # desirable output # {'input': [{ # 'name': 'myinput', # 'required': False, # 'default': 'mytext' # }]} # "default" key moved to the root of the dictionary (inside list) -- ___ Python tracker <https://bugs.python.org/issue34409> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue34409] Add a way to customize iteration over fields in asdict() for the nested dataclasses
New submission from mkurnikov : Suppose I have two dataclasses: @dataclass class NestedDataclass(object): name: str options: Dict[str, Any] = field(default_factory=dict) @dataclass class RootDataclass(object): nested_list: List[NestedDataclass] I want a dict under the key "options" to be merged in the NestedDataclass dict in the dataclasses.asdict(root_dcls_instance). For that, according to docs, I need to specify dict_factory= for dataclasses.asdict() function. The problem is that, according to the implementation, when this function "meets" dataclass, there's no way to customize how result dict will be built. Dataclass itself is never passed to the function. if _is_dataclass_instance(obj): result = [] for f in fields(obj): value = _asdict_inner(getattr(obj, f.name), dict_factory) result.append((f.name, value)) return dict_factory(result) Yes, I can catch "result" obj (what I did in the end): def root_dataclass_dict_factory(obj): if isinstance(obj, list): dataclass_dict = dict(obj) if 'options' in dataclass_dict: dataclass_dict.update(dataclass_dict.pop('options')) return dict(obj) The problem is that type of the dataclass is lost for the list, and if by any chance later I'll have "options" key in the RootDataclass, there's no way for me to distinguish between them cleanly. Other solution is to iterate over the RootDataclass dictionary, follow the path to the NestedDataclass and change dictionary there, but it even uglier. Would be nice to be able to somehow hook into the field traversal of the dataclass instance. -- components: Library (Lib) messages: 323542 nosy: mkurnikov priority: normal severity: normal status: open title: Add a way to customize iteration over fields in asdict() for the nested dataclasses type: enhancement versions: Python 3.6 ___ Python tracker <https://bugs.python.org/issue34409> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com