从外部网络服务中,我收到一个如下所示的 JSON 响应(为了方便起见,下面已经对其进行了反序列化):
alist = [
{
'type': 'type1',
'name': 'dummy',
'oid': 'some_id'
},
{
'type': 'type2',
'name': 'bigdummy',
'anumber': 10
}
]
对于每种类型
都存在一个类。我想要的是实例化相应类的对象,而不使用一长串的 if-elif
。
我尝试如下:
使用 定义类
A
(对于 type1
)和类 B
(对于 type2
) from_dict类方法
:
class A():
def __init__(self, name, oid, optional_stuff=None):
self.name = name
self.oid = oid
self.optional_stuff = optional_stuff
@classmethod
def from_dict(cls, d):
name = d['name']
oid = d['oid']
optional_stuff = d.get('optional_stuff')
return cls(name, oid, optional_stuff)
def foo(self):
print('i am class A')
class B():
def __init__(self, name, anumber):
self.name = name
self.number = anumber
@classmethod
def from_dict(cls, d):
name = d['name']
anumber = d['anumber']
return cls(name, anumber)
然后我定义一个映射字典:
string_class_map = {
'type1': A,
'type2': B
}
最后将 alist
转换为 from_dict
函数可以轻松使用的内容:
alist2 = [
{
di['type']: {k: v for k, v in di.items() if k != 'type'}
}
for di in alist
]
[{'type1': {'name': 'dummy', 'oid': 'some_id'}},
{'type2': {'name': 'bigdummy', 'anumber': 10}}]
object_list = [
string_class_map[k].from_dict(v) for d in alist2 for k, v in d.items()
]
这给了我想要的输出;当我这样做时:
a = object_list[0]
a.name
确实会打印'dummy'
。
问题是是否有更好的方法从alist
(我无法更改此输入)到object_list
。
最佳答案
只要参数名称完全匹配,您就不需要
from_dict
类方法 - 尽管您可能仍然更喜欢通过它们来添加额外的错误处理。我们所做的就是使用参数解包。我将首先总结创建单个对象的过程。也就是说,单个“from_dict”-y 方法应该处理类型的确定、准备其他参数的字典以及调用类工厂。
为这些从工厂创建的类提供一个基类似乎很有用 - 毕竟它们至少有一个共同点,即可以通过这种方式创建;您可以在该级别添加调试内容;对于工厂逻辑本身来说,这是一个方便的地方。
您可以使用装饰器或元类来处理查找映射的创建,以避免需要维护单独的数据 block 。
把它们放在一起,我得到:
class JsonLoadable:
_factory = {}
def __str__(self):
return f'{self.__class__.__name__}(**{{{self.__dict__}}})'
@staticmethod # this is our decorator.
def register(cls):
# We use the class' __name__ attribute to get the lookup key.
# So as long as the classes are named to match the JSON, this
# automatically builds the correct mapping.
JsonLoadable._factory[cls.__name__] = cls
return cls
@staticmethod
def from_dict(d):
d = d.copy()
cls = JsonLoadable._factory[d.pop('type')]
# this is the magic that lets us avoid class-specific logic.
return cls(**d)
# I'm pretty sure there's a way to streamline this further with metaclasses,
# but I'm not up to figuring it out at the moment...
@JsonLoadable.register
class A(JsonLoadable):
def __init__(self, name, oid, optional_stuff=None):
self.name = name
self.oid = oid
self.optional_stuff = optional_stuff
@JsonLoadable.register
class B(JsonLoadable):
def __init__(self, name, anumber):
self.name = name
self.number = anumber
# And now our usage is simple:
objects = [JsonLoadable.from_dict(d) for d in alist]
关于python - 如何从字典列表中实例化不同类的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57236227/