python - 如何从字典列表中实例化不同类的对象?

标签 python oop

从外部网络服务中,我收到一个如下所示的 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/

相关文章:

javascript - JavaScript 中的对象属性 "Previous Value"

python - ndarray的快速突变(替换numpy ndarray的部分)

python - 从gitlab-ci推送到gcr.io,storage.buckets.在存储管理中获取权限

python - 将简单标记的结果放入变量中

php - 使用设备对象的 PHP 类

javascript - 单独适用于页面上多个元素的方法

java - 如何修复Java中的 "looping and candidate voting"错误

python - 将每一行转换为数据框并连接结果

Python Pandas 将 NaN 行替换为另一个数据帧中具有相同日期索引的行

c# - 遍历枚举是一项单一职责吗?