如何一般地序列化和反序列化字典中的对象
How to generically serialize and de-serialize objects from dictionaries
在编写一个小服务时,我发现我想从字典中构建 Python 对象,然后 "serialize" 将它们返回到字典中。
目前我的问题是混合使用 itemgetter
和 vars
的结果。没有给我预期的结果。更具体地说:
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
的静态方法,它应该接受字典和 return 一个 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"])
最终我想要一个通用方法,它接受 class 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}
并按预期设置属性(不是单个元素元组!)。
这可能吗?甚至是个好主意?
一种方法是使用 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
最后一种方法(可能是最干净的方法)是尝试使用 Marshmallow,它将为您处理序列化和反序列化。
https://marshmallow.readthedocs.io/en/3.0/api_reference.html
对于您所描述的内容,您应该使用 class 方法。您要求输入字典具有与 __init__
函数中的参数相同的键,但除此之外,您只是将 key-value 对推送到对象。
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
在编写一个小服务时,我发现我想从字典中构建 Python 对象,然后 "serialize" 将它们返回到字典中。
目前我的问题是混合使用 itemgetter
和 vars
的结果。没有给我预期的结果。更具体地说:
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
的静态方法,它应该接受字典和 return 一个 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"])
最终我想要一个通用方法,它接受 class 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}
并按预期设置属性(不是单个元素元组!)。
这可能吗?甚至是个好主意?
一种方法是使用 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
最后一种方法(可能是最干净的方法)是尝试使用 Marshmallow,它将为您处理序列化和反序列化。
https://marshmallow.readthedocs.io/en/3.0/api_reference.html
对于您所描述的内容,您应该使用 class 方法。您要求输入字典具有与 __init__
函数中的参数相同的键,但除此之外,您只是将 key-value 对推送到对象。
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