Dynamically add fields to dataclass objects
Solution 1
You could use make_dataclass
to create X
on the fly:
X = make_dataclass('X', [('i', int), ('s', str)])
x = X(i=42, s='text')
asdict(x)
# {'i': 42, 's': 'text'}
Or as a derived class:
@dataclass
class X:
i: int
x = X(i=42)
x.__class__ = make_dataclass('Y', fields=[('s', str)], bases=(X,))
x.s = 'text'
asdict(x)
# {'i': 42, 's': 'text'}
Solution 2
As mentioned, fields marked as optional should resolve the issue. If not, consider using properties in dataclasses
. Yep, regular properties should work well enough - though you'll have to declare field in __post_init__
, and that's slightly inconvenient.
If you want to set a default value for the property so accessing getter immediately after creating the object works fine, and if you also want to be able to set a default value via constructor, you can make use of a concept called field properties; a couple libraries like dataclass-wizard provide full support for that.
example usage:
from dataclasses import asdict, dataclass
from typing import Optional
from dataclass_wizard import property_wizard
@dataclass
class X(metaclass=property_wizard):
i: int
s: Optional[str] = None
@property
def _s(self):
return self._s
@_s.setter
def _s(self, s: str):
self._s = s
x = X(i=42)
x.s = 'text'
x
# X(i=42, s='text')
x.s
# 'text'
asdict(x)
# {'i': 42, 's': 'text'}
Disclaimer: I am the creator (and maintener) of this library.
Related videos on Youtube
rominf
Updated on June 04, 2022Comments
-
rominf almost 2 years
I'm writing a library to access REST API. It returns json with user object. I convert it to dict, and then convert it to dataclass object. The problem is that not all fields are fixed. I want to add additional fields (which are not specified in my dataclass) dynamically. I can simply assign values to my object, but they don't appear in the object representation and
dataclasses.asdict
function doesn't add them into resulting dict:from dataclasses import asdict, dataclass @dataclass class X: i: int x = X(i=42) x.s = 'text' x # X(i=42) x.s # 'text' asdict(x) # {'i': 42}