Reputation: 3041
In documentation of Python module attrs
stated that there is a method to convert attributes’ class into dictionary representation:
Example:
>>> @attr.s
... class Coordinates(object):
... x = attr.ib()
... y = attr.ib()
...
>>> attr.asdict(Coordinates(x=1, y=2))
{'x': 1, 'y': 2}
How can I achieve the opposite, instantiating the Coordinates
from its valid dictionary representation without boilerplate and with the joy of the attrs
module?
Upvotes: 17
Views: 9893
Reputation: 136475
Modern versions of attrs
library (I teseted with attrs-23.2.0
) implement __getstate__
and __setstate__
functions for serialization with pickle
. These functions return and accept the state dictionary.
Here is a little example unit-test for that:
def test_attrs():
from attrs import define
@define
class Coordinates:
x: int
y: int
c0 = Coordinates(x=1, y=0)
c1 = Coordinates(x=0, y=1)
assert c0 != c1
d0 = c0.__getstate__()
assert isinstance(d0, dict)
c1.__setstate__(d0)
assert c0 == c1
Upvotes: 0
Reputation: 1648
As a more universal solution, which works with attrs nested classes, enums or any other type annotated structures you can use https://github.com/Tinche/cattrs. It also supports structure/unstructure customization by defining the structure/unstructure hooks
Example:
import attr, cattr
@attr.s(slots=True, frozen=True) # It works with normal classes too.
class C:
a = attr.ib()
b = attr.ib()
instance = C(1, 'a')
cattr.unstructure(instance)
# {'a': 1, 'b': 'a'}
cattr.structure({'a': 1, 'b': 'a'}, C)
# C(a=1, b='a')
Upvotes: 12
Reputation: 3041
Apparently as easy as using dictionary unpacking (double star) operator in corresponding attrs
class instantiation.
Example:
>>> Coordinates(**{'x': 1, 'y': 2})
Coordinates(x=1, y=2)
Upvotes: 19
Reputation: 31
I do this in my web app to be able to serialize/de-serialize into JSON:
First I made a method on my classes that returns a more serializing friendly version:
def asdict(self, serializable=True):
if serializable:
as_dict['type'] = self.__class__.__name__
return as_dict
else:
return attr.asdict(self)
Then when I need to convert one of these dictionaries (JSON objects actually) back into a class instance:
obj_type = values.pop('type')
if obj_type in obj_list:
obj = getattr(sys.modules[__name__], obj_type)(**values)
Upvotes: 3