如何将Python dict转换为特定类型的对象?

时间:2014-06-18 16:36:39

标签: python dictionary metaprogramming

使用Python 2.7我需要将字典转换为类型化对象。

例如,如果我有这个词:

mapy = {'id': 1, 'name': 'bob'}

我需要一些方法在运行时根据上一个映射和类型名称(在此示例中为'Person')生成此类:

class Person:
    def __init__(self):
        self.id = id
        self.name = name

我应该考虑使用元编程技术吗?如果是的话,我应该考虑哪一种(装饰器,元类,......)?

请注意,我不仅需要将字典转换为对象;我还需要将一些类型信息附加到结果对象。

4 个答案:

答案 0 :(得分:6)

您可以尝试namedtuple

from collections import namedtuple

mapy = { 'id' : 1, 'name' : 'bob'}

# Note that the order of `mapy.keys()` is unpredictable.
Person = namedtuple('Person', mapy.keys())

p = Person(**mapy)
p.id
p.name

答案 1 :(得分:2)

您的请求有问题:“我不仅需要将dict转换为对象;还需要将某些类型信息附加到生成的对象中。”问题是“某些类型信息”太模糊了。最文字的实现将为每个对象创建一个新类,这几乎肯定不是你想要的。

试试这个:

def make_dict_into_object(name, d):
    class Dummy(object):
        def __init__(self, attr):
            self.__dict__.update(attr)
    Dummy.__name__ = name

    return Dummy(d)   

mapy = { 'id' : 1, 'name' : 'bob'}
bob = make_dict_into_object('Person', mapy)
print bob.name

mapx = { 'id' : 1, 'name' : 'steve'}
steve = make_dict_into_object('Person', mapx)
print steve.id

到目前为止这么好,对吗?但这可能想要你想要的。运行上面的内容后,添加:

print type(steve) == type(bob)

结果将是 False ,因为make_dict_into_class会在每次调用时生成一个新类。将“某些类型信息”附加到对象上只是愚蠢,因为它是每个对象的新类型。

有多种方法可以解决此问题,具体取决于生成对象的实际用例。一种方法是缓存新创建的类,并假设后续具有相同名称的调用引用同一个类:

class_cache = {}
def make_dict_into_object(name, d):
    the_class = class_cache.setdefault(name, None)
    if the_class is None:
        class the_class(object):
            def __init__(self, attr):
                self.__dict__.update(attr)
        the_class.__name__ = name
        class_cache[name]=the_class

    return the_class(d)   

使用此版本,bobsteve最终会使用相同的类型:

mapy = { 'id' : 1, 'name' : 'bob'}
bob = make_dict_into_class('Person', mapy)
print bob.name

mapx = { 'id' : 1, 'name' : 'steve'}
steve = make_dict_into_class('Person', mapx)
print steve.id

print type(steve) == type(bob)

答案 2 :(得分:1)

通常,您的__init__会有实际参数来提供属性值:

class Person:
    def __init__(self, id, name):
        self.id = id
        self.name = name

然后你可以直接将字典解压缩到它:

>>> p = Person(**{'id': 1, 'name': 'bob'})
>>> p.id
1
>>> p.name
'bob'
>>> p
<__main__.Person object at 0x02DA5730>

答案 3 :(得分:1)

您可以使用以下函数根据给定的__init__生成dict方法:

def make_init(d):
    def __init__(self, **kwargs):
        for name in d:
            setattr(self, name, kwargs[name])
    return __init__

然后,使用type的3参数形式在运行时创建一个类。

type_name = "Person"
Person = type(type_name, (object,), { '__init__': make_init(mapy) })

最后,使用字典实例化类以提供__init__的参数:

obj = Person(**mapy)