复杂地将嵌套字典转换为python中的对象

时间:2011-03-13 16:29:37

标签: python

我开始学习python已经很久了,但我真的想深入研究它。并努力挖掘。所以这是我一直在研究的一项任务,但尚未解决:
我给出了嵌套字典和列表的混合组合(我们称之为“组合”),我需要实现 function ,它允许将嵌套元素作为对象属性访问,还以某种方式将组合元素视为可迭代的。这应该是这样的:

combination = {
'item1': 3.14,
'item2': 42,
'items': [
         'text text text',
         {
             'field1': 'a',
             'field2': 'b',
         },
         {
             'field1': 'c',
             'field2': 'd',
         },
         ]
}

def function(combination):
    ...

这样 list(function(combination).items.field1) 将给出:['a', 'c']
list(function(combination).item1)会:[3.14] 编辑正如@FM所提到的,我错过了处理非dict元素的描述: list(function(combination).items[0])>>> ['text text text']


我尝试实现一个类(感谢Marc)来帮助我:

class Struct:
    def __init__(self, **entries): 
        self.__dict__.update(entries)

然后在功能中使用它,例如return Struct(**combination)
虽然非常漂亮,但这只是达到预期效果的第一步 但是,随着下一步需要更深入,它压倒了我,我无法自己做到这一点 因此,我请求你的帮助。

迈克尔。

4 个答案:

答案 0 :(得分:6)

怎么样:

class ComboParser(object):
    def __init__(self,data):
        self.data=data
    def __getattr__(self,key):
        try:
            return ComboParser(self.data[key])
        except TypeError:
            result=[]
            for item in self.data:
                if key in item:
                    try:
                        result.append(item[key])
                    except TypeError: pass
            return ComboParser(result)
    def __getitem__(self,key):
        return ComboParser(self.data[key])
    def __iter__(self):
        if isinstance(self.data,basestring):
            # self.data might be a str or unicode object
            yield self.data
        else:
            # self.data might be a list or tuple
            try:
                for item in self.data:
                    yield item
            except TypeError:
                # self.data might be an int or float
                yield self.data
    def __length_hint__(self):
        return len(self.data)

产生:

combination = {
    'item1': 3.14,
    'item2': 42,
    'items': [
        'text text text',
        {
            'field1': 'a',
            'field2': 'b',
            },
        {
            'field1': 'c',
            'field2': 'd',
            },
        {
            'field1': 'e',
            'field3': 'f',
            },        
        ]
    }
print(list(ComboParser(combination).item1))
# [3.1400000000000001]
print(list(ComboParser(combination).items))
# ['text text text', {'field2': 'b', 'field1': 'a'}, {'field2': 'd', 'field1': 'c'}, {'field3': 'f', 'field1': 'e'}]
print(list(ComboParser(combination).items[0]))
# ['text text text']
print(list(ComboParser(combination).items.field1))
# ['a', 'c', 'e']

答案 1 :(得分:3)

例如:

class Struct:
    def __init__(self, **entries):
        for key, value in entries.items():
            value2 = (Struct(**value) if isinstance(value, dict) else value)
            self.__dict__[key] = value2

entries = {
    "a": 1,
    "b": {
        "c": {
            "d": 2
        }
    }
}

obj = Struct(**entries)
print(obj.a) #1
print(obj.b.c.d) #2

答案 2 :(得分:1)

我认为这里有两个选择:

  1. function将嵌套数据结构转换为一系列链接在一​​起的对象,这些对象实现了支持list()dict()的协议(对象必须实现许多功能) ,至少包括__iter____len____getitem__等。要创建对象,您需要定义实现这些行为的类,并递归地组装它们,或者使用type()动态创建类。

  2. 使function返回代理访问基础数据结构的类。要实现允许非实际成员访问成员属性(即执行function(combination).items)的类,您可以覆盖__getattr__。您将无法访问“完整虚线路径”,因此可以在此函数的任何单个调用中使用,因此它必须递归操作并在虚线路径的每个级别返回其他实例。我相信这种方法比第一种方法简单。

答案 3 :(得分:1)

您可能需要做的是查看您分配给对象__dict__的每个项目,看它是否是一个字典或可迭代的。

import types
class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)
        for k,v in self.__dict__.items():
            if type(v) == types.DictType:
                setattr(self, k, Struct(**v))

这样你就可以使用递归安排了。看起来像这样:

>>> b = Struct(a=1, b={'a':1})
>>> b.b.a
1