如何从字典中一般性地序列化和反序列化对象

时间:2019-06-18 00:46:01

标签: python dictionary generic-programming

在编写一些服务时,我发现我想从字典中构建Python对象,然后将它们“序列化”回字典中。

当前,我的问题是使用itemgettervars混合使用的结果。没有给我预期的结果。更具体地说:

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))

因此,我们有一个称为from_dict的静态方法,该方法应带一个字典并返回一个Object对象。

这是我想要的行为:

>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": "frobulate", "bar": 42}

但是,我得到的却是:

>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": ("frobulate",), "bar": (42,)}

其中属性设置为单元素元组。

是否有一种干净的python方式让我得到我期望的行为?

我正在努力避免重复:

class Object(object):
    ...
    @staticmethod
    def from_dict(dictionary):
        return Object(foo=dictionary["foo"],bar=dictionary["bar"])

最终,我希望有一个通用方法,该方法采用一个类cls并可以解析字典中的任何对象:

def parse_object_from_dict(cls, attributes, dictionary):
    return cls(*itemgetter(*attributes)(dictionary))

可以这样使用:

>> foo = parse_object_from_dict(Foo, ["a", "b"], {"a": 42, "b": 42})
>> obj = parse_object_from_dict(Object, ["foo", "bar"], {"foo": 42, "bar": 42}

并按预期设置属性(而不是单元素元组!)。

这可能吗?甚至是个好主意?

2 个答案:

答案 0 :(得分:2)

对于所描述的内容,应该使用类方法。您需要输入字典具有与__init__函数中的参数相同的键,但是除此之外,您还只是将键值对推入对象。

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

答案 1 :(得分:1)

一种方法是使用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

另一种方法是使用**,但可能做事草率,并且如果字典中包含多余的键,则可能引发错误。

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

最后一种可能是最干净的方法是尝试使用棉花糖,它可以为您处理序列化和反序列化。

https://marshmallow.readthedocs.io/en/3.0/api_reference.html