-
-
Notifications
You must be signed in to change notification settings - Fork 125
Description
I have a seemingly simple problem: I have an attrs class that I want to serialize to a Python dictionary, but I want to be able to prune outputs if they're None or equal to their default values, e.g:
@attrs.define
class Example:
a: int
b: int | None
c: int = 123
def to_dict(self, exclude_none: bool, exclude_defaults: bool) -> dict:
... # TODO
e = Example(a=100, b=None)
e.to_dict(exclude_none=False, exclude_defaults=False) # {'a': 100, 'b': None, 'c': 123}
e.to_dict(exclude_none=True, exclude_defaults=False) # {'a': 100, 'c': 123}
e.to_dict(exclude_none=False, exclude_defaults=True) # {'a': 100, 'b': None}
e.to_dict(exclude_none=True, exclude_defaults=True) # {'a': 100}omit_if_default seems to do half of what I want already, and I was hopeful that I could do something like:
converter = cattrs.Converter(omit_if_default=True)
class Example:
...
def to_dict(self, exclude_none: bool, exclude_defaults: bool) -> dict:
converter.omit_if_default = exclude_defaults
return converter.unstructure(self)But after taking a closer look, it seems the unstructure function is only generated once with the current value of omit_if_default, and then cached for reuse (for performance reasons I imagine). Does this imply that the "correct" way to do this is to create 4 separate hooks for every possible class I want to unstructure, and then select between them based on the passed in arguments?
If push comes to shove I can just generate the maximum dict and then manually prune myself:
converter = cattrs.Converter(omit_if_default=False)
class Example:
...
def to_dict(self, exclude_none: bool, exclude_defaults: bool) -> dict:
defaults = {field.name: field.default for field in attrs.fields(type(self))}
return {
k: v for k, v in converter.unstructure(self, type(self)).items()
if not ((v is None and exclude_none) or (getattr(self, k) == defaults[k] and exclude_defaults))
}But this doesn't support removing None/defaults recursively through the structure, and it feels like I'm circumventing cattrs instead of properly using it.