Reputation: 341
While writing a little service I found that I'd like to build Python objects from dictionaries and then "serialize" them back into dictionaries.
Currently my problem is the result of doing so using a mix of itemgetter
and vars
. Not giving me the expected result. More concretely:
from operator import itemgetter
class Object(object):
def __init__(self, foo: str, bar: int):
self.foo, self.bar = foo, bar
@staticmethod
def from_dict(dictionary):
attributes = ["foo", "bar"]
return Object(*itemgetter(*attributes)(dictionary))
So we have this static method called from_dict
that should take a dictionary and return a Object
object.
This is the behavior I would like:
>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": "frobulate", "bar": 42}
However, what I get instead is:
>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": ("frobulate",), "bar": (42,)}
Where the attributes are set as single element tuples.
Is there a clean python way of getting this give me the behavior I expect?
I'm trying to avoid duplication like:
class Object(object):
...
@staticmethod
def from_dict(dictionary):
return Object(foo=dictionary["foo"],bar=dictionary["bar"])
Eventually I'd like to have a generic method that takes a class cls
and can parse any object from a dictionary:
def parse_object_from_dict(cls, attributes, dictionary):
return cls(*itemgetter(*attributes)(dictionary))
That could be used like:
>> foo = parse_object_from_dict(Foo, ["a", "b"], {"a": 42, "b": 42})
>> obj = parse_object_from_dict(Object, ["foo", "bar"], {"foo": 42, "bar": 42}
And have the attributes set as expected (not as single element tuples!).
Is this possible? Or even a good idea?
Upvotes: 0
Views: 1311
Reputation: 36623
For what you are describing, you should be using a class method. You are requiring that the input dictionary have the same keys as the arguments in the __init__
function, but beyond that you are just pushing the key-value pairs to the object.
class Object:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
@classmethod
def from_dict(cls, d):
ob = cls(**d)
for k,v in d.items():
setattr(ob, str(k), v)
return ob
Upvotes: 2
Reputation: 1508
One way to do this is with setattr
class Object(object):
def __init__(self, foo=None, bar=None):
self.foo = foo
self.bar = bar
@staticmethod
def from_dict(dic):
obj = Object()
[setattr(obj, attr, dic[attr]) for attr in ["foo", "bar"]]
return obj
Another way is using **
but can be a sloppy way to do things and can throw errors if your dictionary contains extra keys.
class Object(object):
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
@staticmethod
def from_dict(dic):
return Object(**dic)
data = {'foo': 'a', 'bar': 2}
o = Object.from_dict(data)
data = {'foo': 'a', 'bar': 2, 'baz': 3}
o = Object.from_dict(data) # this will throw an error due to the 'baz' key
One final way to do it, which is probably the cleanest way, is to try using Marshmallow, which will handle serialization and deserialization for you.
https://marshmallow.readthedocs.io/en/3.0/api_reference.html
Upvotes: 1