【问题标题】:Convert dict to class dynamically in python在python中动态地将dict转换为类
【发布时间】:2016-11-08 18:55:16
【问题描述】:

我知道以前已经回答过类似的问题,但是在浏览网页数小时后,我仍然无法正确解决我的问题(或“python-way”解决方案)

我正在使用以下“数据库”模块将任何对象插入 MongoDB:

from pymongo import MongoClient
import json

def insert(obj, collection_name):
    with MongoClient(27017, 'localhost') as client:
        if hasattr(obj, '__dict__'):
            doc = obj.__dict__
        else:
            doc = json.dumps(obj)
        return client['test'][collection_name].insert_one(doc).inserted_id

def get_one(collection_name, query):
    with MongoClient(27017, 'localhost') as client:
        return client['test'][collection_name].find_one(query)

这部分效果很好,因为我可以处理任何模块中的任何类并以通用方式存储对象。

我面临的问题是从 MongoDB 重新加载一个对象并将其转换回正确的类。

我有一个通用类“操作”(代表可以对银行/货币帐户执行的操作:

import database 

class Operation(object):
    def __init__(self):
        self._id = 'OPE-{}'.format(str(uuid.uuid1()))

    @property
    def id(self):
        return self._id

    def save(self):
        database.update(self, 'operations')

def create_from_dict(d):
    operation = Operation()
    for key, value in d.items():
        operation.__dict__[key] = value
    return operation

def get(operation_id):
    return create_from_dict(database.get_one('operations', {'_id': operation_id}))

直到此时一切正常,我可以实例化任何 Operation 对象,将其插入 MongoDB 并使用它的 id 将其加载回来。

现在我有两个从“操作”派生的新类“费用”和“信用”

import uuid
from operation import Operation

class Credit(Operation):
    def __init__(self):
        Operation.__init__(self)
        self._id = 'CRE-{}'.format(uuid.uuid1())

    @property
    def id(self):
        return self._id

import uuid
from operation import Operation

class Fee(Operation):
    def __init__(self):
        Operation.__init__(self)
        self._id = 'FEE-{}'.format(uuid.uuid1())

    @property
    def id(self):
        return self._id

我们的想法是保留在“操作”类中保存/加载对象的代码,并在需要时创建简单的派生类。 所以“Operation”类应该能够动态地将加载的对象从 Operation 转换为 Credit/Fee/Transfer...

我试图做的是在所有类上添加以下行以存储对象类型:

self.type = self.__class__

或者(因为类不能直接序列化为json)

self.type = self.__name__

但我不知道如何实现:

def create_from_dict(d):
    operation = Operation()
    for key, value in d.items():
        operation.__dict__[key] = value
    operation.__class__ = get_type_from_name(operation.type) #This is the function I'm trying to implement
    return operation

在对对象进行编码以将其从另一个模块转换回时,我是否应该同时存储模块和类名?

很抱歉这个问题很长,但我想完整描述我的代码背后的逻辑 我对任何建议都非常开放!

编辑: locals() 或 globals() 可以走吗?您将类名传递给 get_type_from_name() 方法并返回相应的类? 高级 Python 专家的另一个问题:这是正确的方法吗?

【问题讨论】:

    标签: python json mongodb dictionary casting


    【解决方案1】:

    使用您建议的self.type = self.__name__ 想法,您可以尝试get_type_from_name 函数的此实现:

    def get_type_from_name(name):
        name_to_type = {'Credit': Credit, 'Fee':Fee}
        return name_to_type[name]
    

    虽然我不太确定我是否完全理解您的问题,所以如果这不是您想要的,请告诉我。

    【讨论】:

    • 这是一个想法,但我宁愿不必维护静态映射名称/类。由于派生类 (Credit, Fee) 依赖于 Operation,因此我试图避免任何循环依赖。我在想的是 Operation 内部的一种方法,它可以在不引用对象类的情况下转换对象。这有意义吗?
    • 看来locals() 确实包含您想要的信息,所以它可能只是可以做return locals()[name] 并完成它,尽管这开始进入一些非常棘手的领域,并且您可能想弄清楚是否没有更好的方法来做您想做的事情。例如,最强大的解决方案是腌制每个对象,然后将其推入 MongoDB 中的二进制字段。或者至少,您可以腌制类本身,并将其存储(这样您就不需要自己从字符串动态重新创建它)。
    【解决方案2】:

    我想我成功了:

    将此添加到我想要(反)序列化的类中:

    self.type = self.__class__.__name__
    self.module = self.__module__
    

    然后一旦从 PyMongo 加载了 json 文档:

        if not hasattr(doc, 'module') or not hasattr(doc, 'type'):
            return obj
        for key, value in doc.items():
            obj.__dict__[key] = value
        mod = __import__(obj.module, fromlist=[obj.type])
        obj.__class__ = getattr(mod, obj.type)
    

    最后两行导入包含类的模块并动态修改对象class

    感谢 user3030010 的回答。 但我还是很想知道你们认为这是一个好的设计还是会导致更多问题。

    【讨论】:

      猜你喜欢
      • 2012-10-15
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 2019-10-08
      • 2013-06-15
      • 2011-08-08
      • 1970-01-01
      相关资源
      最近更新 更多