首页 文章

将嵌套的Python dict转换为对象?

提问于
浏览
468

我正在寻找一种优雅的方法,使用一些嵌套的dicts和列表(即javascript样式的对象语法)在dict上使用属性访问来获取数据 .

例如:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

应该可以通过这种方式访问:

>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
bar

我认为,如果没有递归,这是不可能的,但是什么是获得dicts的对象样式的好方法?

30 回答

  • 12

    有一个名为namedtuple的集合助手,可以为你做到这一点:

    from collections import namedtuple
    
    d_named = namedtuple('Struct', d.keys())(*d.values())
    
    In [7]: d_named
    Out[7]: Struct(a=1, b={'c': 2}, d=['hi', {'foo': 'bar'}])
    
    In [8]: d_named.a
    Out[8]: 1
    
  • 24

    Update: 在Python 2.6及更高版本中,考虑namedtuple数据结构是否适合您的需求:

    >>> from collections import namedtuple
    >>> MyStruct = namedtuple('MyStruct', 'a b d')
    >>> s = MyStruct(a=1, b={'c': 2}, d=['hi'])
    >>> s
    MyStruct(a=1, b={'c': 2}, d=['hi'])
    >>> s.a
    1
    >>> s.b
    {'c': 2}
    >>> s.c
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MyStruct' object has no attribute 'c'
    >>> s.d
    ['hi']
    

    替代方案(原始答案内容)是:

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

    然后,您可以使用:

    >>> args = {'a': 1, 'b': 2}
    >>> s = Struct(**args)
    >>> s
    <__main__.Struct instance at 0x01D6A738>
    >>> s.a
    1
    >>> s.b
    2
    
  • 9
    class obj(object):
        def __init__(self, d):
            for a, b in d.items():
                if isinstance(b, (list, tuple)):
                   setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
                else:
                   setattr(self, a, obj(b) if isinstance(b, dict) else b)
    
    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> x = obj(d)
    >>> x.b.c
    2
    >>> x.d[1].foo
    'bar'
    
  • 1

    令人惊讶的是没有人提到Bunch . 这个库专门用于提供对dict对象的属性样式访问,并且完全符合OP的要求 . 示范:

    >>> from bunch import bunchify
    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> x = bunchify(d)
    >>> x.a
    1
    >>> x.b.c
    2
    >>> x.d[1].foo
    'bar'
    

    一个Python 3库可以在https://github.com/Infinidat/munch获得 - Credit to codyzu

  • 79
    x = type('new_dict', (object,), d)
    

    然后添加递归到这个,你就完成了 .

    edit 这就是我实现它的方式:

    >>> d
    {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
    >>> def obj_dic(d):
        top = type('new', (object,), d)
        seqs = tuple, list, set, frozenset
        for i, j in d.items():
            if isinstance(j, dict):
                setattr(top, i, obj_dic(j))
            elif isinstance(j, seqs):
                setattr(top, i, 
                    type(j)(obj_dic(sj) if isinstance(sj, dict) else sj for sj in j))
            else:
                setattr(top, i, j)
        return top
    
    >>> x = obj_dic(d)
    >>> x.a
    1
    >>> x.b.c
    2
    >>> x.d[1].foo
    'bar'
    
  • 2

    我认为这是前面例子中最好的方面,这就是我想出的:

    class Struct:
      '''The recursive class for building and representing objects with.'''
      def __init__(self, obj):
        for k, v in obj.iteritems():
          if isinstance(v, dict):
            setattr(self, k, Struct(v))
          else:
            setattr(self, k, v)
      def __getitem__(self, val):
        return self.__dict__[val]
      def __repr__(self):
        return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for
          (k, v) in self.__dict__.iteritems()))
    
  • 14
    class Struct(object):
        """Comment removed"""
        def __init__(self, data):
            for name, value in data.iteritems():
                setattr(self, name, self._wrap(value))
    
        def _wrap(self, value):
            if isinstance(value, (tuple, list, set, frozenset)): 
                return type(value)([self._wrap(v) for v in value])
            else:
                return Struct(value) if isinstance(value, dict) else value
    

    可以与任何深度的任何序列/字典/值结构一起使用 .

  • 2

    如果您的dict来自 json.loads() ,您可以在一行中将其转换为对象(而不是dict):

    import json
    from collections import namedtuple
    
    json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
    

    另见How to convert JSON data into a Python object .

  • 55

    如果你想要将dict键作为一个对象(或者作为一个困难键的dict)来访问,那么递归执行它,并且还能够更新原始dict,你可以这样做:

    class Dictate(object):
        """Object view of a dict, updating the passed in dict when values are set
        or deleted. "Dictate" the contents of a dict...: """
    
        def __init__(self, d):
            # since __setattr__ is overridden, self.__dict = d doesn't work
            object.__setattr__(self, '_Dictate__dict', d)
    
        # Dictionary-like access / updates
        def __getitem__(self, name):
            value = self.__dict[name]
            if isinstance(value, dict):  # recursively view sub-dicts as objects
                value = Dictate(value)
            return value
    
        def __setitem__(self, name, value):
            self.__dict[name] = value
        def __delitem__(self, name):
            del self.__dict[name]
    
        # Object-like access / updates
        def __getattr__(self, name):
            return self[name]
    
        def __setattr__(self, name, value):
            self[name] = value
        def __delattr__(self, name):
            del self[name]
    
        def __repr__(self):
            return "%s(%r)" % (type(self).__name__, self.__dict)
        def __str__(self):
            return str(self.__dict)
    

    用法示例:

    d = {'a': 'b', 1: 2}
    dd = Dictate(d)
    assert dd.a == 'b'  # Access like an object
    assert dd[1] == 2  # Access like a dict
    # Updates affect d
    dd.c = 'd'
    assert d['c'] == 'd'
    del dd.a
    del dd[1]
    # Inner dicts are mapped
    dd.e = {}
    dd.e.f = 'g'
    assert dd['e'].f == 'g'
    assert d == {'c': 'd', 'e': {'f': 'g'}}
    
  • 6
    >>> def dict2obj(d):
            if isinstance(d, list):
                d = [dict2obj(x) for x in d]
            if not isinstance(d, dict):
                return d
            class C(object):
                pass
            o = C()
            for k in d:
                o.__dict__[k] = dict2obj(d[k])
            return o
    
    
    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> x = dict2obj(d)
    >>> x.a
    1
    >>> x.b.c
    2
    >>> x.d[1].foo
    'bar'
    
  • 2

    我最终尝试了AttrDictBunch库,发现它们对我的使用来说太慢了 . 在我和朋友调查之后,我们发现编写这些库的主要方法导致库通过嵌套对象进行主动递归,并在整个过程中复制字典对象 . 考虑到这一点,我们做了两个关键的改变 . 1)我们使属性延迟加载2)而不是创建字典对象的副本,我们创建轻量级代理对象的副本 . 这是最终的实施 . 使用此代码的性能提升令人难以置信 . 当使用AttrDict或Bunch时,这两个库分别占用了我的请求时间的1/2和1/3(什么!?) . 这段代码将时间缩短到几乎为零(在0.5ms的范围内) . 这当然取决于您的需求,但如果您在代码中使用此功能,请务必使用这样的简单方法 .

    class DictProxy(object):
        def __init__(self, obj):
            self.obj = obj
    
        def __getitem__(self, key):
            return wrap(self.obj[key])
    
        def __getattr__(self, key):
            try:
                return wrap(getattr(self.obj, key))
            except AttributeError:
                try:
                    return self[key]
                except KeyError:
                    raise AttributeError(key)
    
        # you probably also want to proxy important list properties along like
        # items(), iteritems() and __len__
    
    class ListProxy(object):
        def __init__(self, obj):
            self.obj = obj
    
        def __getitem__(self, key):
            return wrap(self.obj[key])
    
        # you probably also want to proxy important list properties along like
        # __iter__ and __len__
    
    def wrap(value):
        if isinstance(value, dict):
            return DictProxy(value)
        if isinstance(value, (tuple, list)):
            return ListProxy(value)
        return value
    

    请参阅https://stackoverflow.com/users/704327/michael-merickel的原始实现here .

    另外需要注意的是,这个实现非常简单,并没有实现您可能需要的所有方法 . 您需要在DictProxy或ListProxy对象上根据需要编写它们 .

  • 3

    x.__dict__.update(d) 应该没问题 .

  • 89

    这应该让你开始:

    class dict2obj(object):
        def __init__(self, d):
            self.__dict__['d'] = d
    
        def __getattr__(self, key):
            value = self.__dict__['d'][key]
            if type(value) == type({}):
                return dict2obj(value)
    
            return value
    
    d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    
    x = dict2obj(d)
    print x.a
    print x.b.c
    print x.d[1].foo
    

    它不需要将列表包装在UserList中并重载 __getitem__ 以包装dicts .

  • 0

    旧的问答,但我得到更多的话题 . 似乎没有人谈论递归字典 . 这是我的代码:

    #!/usr/bin/env python
    
    class Object( dict ):
        def __init__( self, data = None ):
            super( Object, self ).__init__()
            if data:
                self.__update( data, {} )
    
        def __update( self, data, did ):
            dataid = id(data)
            did[ dataid ] = self
    
            for k in data:
                dkid = id(data[k])
                if did.has_key(dkid):
                    self[k] = did[dkid]
                elif isinstance( data[k], Object ):
                    self[k] = data[k]
                elif isinstance( data[k], dict ):
                    obj = Object()
                    obj.__update( data[k], did )
                    self[k] = obj
                    obj = None
                else:
                    self[k] = data[k]
    
        def __getattr__( self, key ):
            return self.get( key, None )
    
        def __setattr__( self, key, value ):
            if isinstance(value,dict):
                self[key] = Object( value )
            else:
                self[key] = value
    
        def update( self, *args ):
            for obj in args:
                for k in obj:
                    if isinstance(obj[k],dict):
                        self[k] = Object( obj[k] )
                    else:
                        self[k] = obj[k]
            return self
    
        def merge( self, *args ):
            for obj in args:
                for k in obj:
                    if self.has_key(k):
                        if isinstance(self[k],list) and isinstance(obj[k],list):
                            self[k] += obj[k]
                        elif isinstance(self[k],list):
                            self[k].append( obj[k] )
                        elif isinstance(obj[k],list):
                            self[k] = [self[k]] + obj[k]
                        elif isinstance(self[k],Object) and isinstance(obj[k],Object):
                            self[k].merge( obj[k] )
                        elif isinstance(self[k],Object) and isinstance(obj[k],dict):
                            self[k].merge( obj[k] )
                        else:
                            self[k] = [ self[k], obj[k] ]
                    else:
                        if isinstance(obj[k],dict):
                            self[k] = Object( obj[k] )
                        else:
                            self[k] = obj[k]
            return self
    
    def test01():
        class UObject( Object ):
            pass
        obj = Object({1:2})
        d = {}
        d.update({
            "a": 1,
            "b": {
                "c": 2,
                "d": [ 3, 4, 5 ],
                "e": [ [6,7], (8,9) ],
                "self": d,
            },
            1: 10,
            "1": 11,
            "obj": obj,
        })
        x = UObject(d)
    
    
        assert x.a == x["a"] == 1
        assert x.b.c == x["b"]["c"] == 2
        assert x.b.d[0] == 3
        assert x.b.d[1] == 4
        assert x.b.e[0][0] == 6
        assert x.b.e[1][0] == 8
        assert x[1] == 10
        assert x["1"] == 11
        assert x[1] != x["1"]
        assert id(x) == id(x.b.self.b.self) == id(x.b.self)
        assert x.b.self.a == x.b.self.b.self.a == 1
    
        x.x = 12
        assert x.x == x["x"] == 12
        x.y = {"a":13,"b":[14,15]}
        assert x.y.a == 13
        assert x.y.b[0] == 14
    
    def test02():
        x = Object({
            "a": {
                "b": 1,
                "c": [ 2, 3 ]
            },
            1: 6,
            2: [ 8, 9 ],
            3: 11,
        })
        y = Object({
            "a": {
                "b": 4,
                "c": [ 5 ]
            },
            1: 7,
            2: 10,
            3: [ 12 , 13 ],
        })
        z = {
            3: 14,
            2: 15,
            "a": {
                "b": 16,
                "c": 17,
            }
        }
        x.merge( y, z )
        assert 2 in x.a.c
        assert 3 in x.a.c
        assert 5 in x.a.c
        assert 1 in x.a.b
        assert 4 in x.a.b
        assert 8 in x[2]
        assert 9 in x[2]
        assert 10 in x[2]
        assert 11 in x[3]
        assert 12 in x[3]
        assert 13 in x[3]
        assert 14 in x[3]
        assert 15 in x[2]
        assert 16 in x.a.b
        assert 17 in x.a.c
    
    if __name__ == '__main__':
        test01()
        test02()
    
  • 3

    您可以使用 custom object hook 来利用标准库的 json module

    import json
    
    class obj(object):
        def __init__(self, dict_):
            self.__dict__.update(dict_)
    
    def dict2obj(d):
        return json.loads(json.dumps(d), object_hook=obj)
    

    用法示例:

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
    >>> o = dict2obj(d)
    >>> o.a
    1
    >>> o.b.c
    2
    >>> o.d[0]
    u'hi'
    >>> o.d[1].foo
    u'bar'
    

    并且 not strictly read-onlynamedtuple 一样,即你可以改变值 - 而不是结构:

    >>> o.b.c = 3
    >>> o.b.c
    3
    
  • 1

    我偶然发现了我需要以递归方式将一个dicts列表转换为对象列表的情况,所以根据Roberto的片段在这里为我做了什么工作:

    def dict2obj(d):
        if isinstance(d, dict):
            n = {}
            for item in d:
                if isinstance(d[item], dict):
                    n[item] = dict2obj(d[item])
                elif isinstance(d[item], (list, tuple)):
                    n[item] = [dict2obj(elem) for elem in d[item]]
                else:
                    n[item] = d[item]
            return type('obj_from_dict', (object,), n)
        elif isinstance(d, (list, tuple,)):
            l = []
            for item in d:
                l.append(dict2obj(item))
            return l
        else:
            return d
    

    请注意,出于显而易见的原因,任何元组都将转换为其等效列表 .

    希望这可以帮助别人,就像你的所有答案一样,对我来说,伙计们 .

  • 0

    想上传我的这个小范例的版本 .

    class Struct(dict):
      def __init__(self,data):
        for key, value in data.items():
          if isinstance(value, dict):
            setattr(self, key, Struct(value))
          else:   
            setattr(self, key, type(value).__init__(value))
    
          dict.__init__(self,data)
    

    它保留了导入到类中的类型的属性 . 我唯一关心的是从字典中覆盖你解析的方法 . 但除此之外看似坚实!

  • 3
    from mock import Mock
    d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    my_data = Mock(**d)
    
    # We got
    # my_data.a == 1
    
  • 6

    dict 分配给空对象的 __dict__ 怎么样?

    class Object:
        """If your dict is "flat", this is a simple way to create an object from a dict
    
        >>> obj = Object()
        >>> obj.__dict__ = d
        >>> d.a
        1
        """
        pass
    

    当然,除非你递归地遍历dict,否则你的嵌套dict示例会失败:

    # For a nested dict, you need to recursively update __dict__
    def dict2obj(d):
        """Convert a dict to an object
    
        >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
        >>> obj = dict2obj(d)
        >>> obj.b.c
        2
        >>> obj.d
        ["hi", {'foo': "bar"}]
        """
        try:
            d = dict(d)
        except (TypeError, ValueError):
            return d
        obj = Object()
        for k, v in d.iteritems():
            obj.__dict__[k] = dict2obj(v)
        return obj
    

    你的示例列表元素可能是 Mapping ,这是(键,值)对的列表,如下所示:

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo': "bar"})]}
    >>> obj = dict2obj(d)
    >>> obj.d.hi.foo
    "bar"
    
  • 601

    我知道这里已经有很多答案了,我迟到了,但是这个方法会递归并且“就地”将字典转换为类似对象的结构...在3.x.x中工作

    def dictToObject(d):
        for k,v in d.items():
            if isinstance(v, dict):
                d[k] = dictToObject(v)
        return namedtuple('object', d.keys())(*d.values())
    
    # Dictionary created from JSON file
    d = {
        'primaryKey': 'id', 
        'metadata': 
            {
                'rows': 0, 
                'lastID': 0
            }, 
        'columns': 
            {
                'col2': {
                    'dataType': 'string', 
                    'name': 'addressLine1'
                }, 
                'col1': {
                    'datatype': 'string', 
                    'name': 'postcode'
                }, 
                'col3': {
                    'dataType': 'string', 
                    'name': 'addressLine2'
                }, 
                'col0': {
                    'datatype': 'integer', 
                    'name': 'id'
                }, 
                'col4': {
                    'dataType': 'string', 
                    'name': 'contactNumber'
                }
            }, 
            'secondaryKeys': {}
    }
    
    d1 = dictToObject(d)
    d1.columns.col1 # == object(datatype='string', name='postcode')
    d1.metadata.rows # == 0
    
  • 4

    这是实现SilentGhost原始建议的另一种方法:

    def dict2obj(d):
      if isinstance(d, dict):
        n = {}
        for item in d:
          if isinstance(d[item], dict):
            n[item] = dict2obj(d[item])
          elif isinstance(d[item], (list, tuple)):
            n[item] = [dict2obj(elem) for elem in d[item]]
          else:
            n[item] = d[item]
        return type('obj_from_dict', (object,), n)
      else:
        return d
    
  • 2

    让我解释一下我前一段时间使用过的解决方案 . 但首先,我没有的原因通过以下代码说明:

    d = {'from': 1}
    x = dict2obj(d)
    
    print x.from
    

    给出了这个错误:

    File "test.py", line 20
        print x.from == 1
                    ^
    SyntaxError: invalid syntax
    

    因为“from”是一个Python关键字,所以你不能使用某些字典键允许 .


    现在我的解决方案允许直接使用他们的名字访问字典项 . 但它也允许你使用“字典语义” . 以下是带有示例用法的代码:

    class dict2obj(dict):
        def __init__(self, dict_):
            super(dict2obj, self).__init__(dict_)
            for key in self:
                item = self[key]
                if isinstance(item, list):
                    for idx, it in enumerate(item):
                        if isinstance(it, dict):
                            item[idx] = dict2obj(it)
                elif isinstance(item, dict):
                    self[key] = dict2obj(item)
    
        def __getattr__(self, key):
            return self[key]
    
    d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    
    x = dict2obj(d)
    
    assert x.a == x['a'] == 1
    assert x.b.c == x['b']['c'] == 2
    assert x.d[1].foo == x['d'][1]['foo'] == "bar"
    
  • 2

    这是另一个实现:

    class DictObj(object):
        def __init__(self, d):
            self.__dict__ = d
    
    def dict_to_obj(d):
        if isinstance(d, (list, tuple)): return map(dict_to_obj, d)
        elif not isinstance(d, dict): return d
        return DictObj(dict((k, dict_to_obj(v)) for (k,v) in d.iteritems()))
    

    [编辑]错过了关于在列表中处理dicts的一点,而不仅仅是其他的dicts . 添加了修复 .

  • 4

    这个怎么样:

    from functools import partial
    d2o=partial(type, "d2o", ())
    

    然后可以这样使用:

    >>> o=d2o({"a" : 5, "b" : 3})
    >>> print o.a
    5
    >>> print o.b
    3
    
  • 3

    我认为dict由数字,字符串和dict组成,大部分时间都足够了 . 所以我忽略了元组,列表和其他类型没有出现在dict的最终维度中的情况 .

    考虑到继承,结合递归,它方便地解决了打印问题,并提供了两种查询数据的方法,一种是编辑数据的方法 .

    请参阅下面的示例,该描述描述了有关学生的一些信息:

    group=["class1","class2","class3","class4",]
    rank=["rank1","rank2","rank3","rank4","rank5",]
    data=["name","sex","height","weight","score"]
    
    #build a dict based on the lists above
    student_dic=dict([(g,dict([(r,dict([(d,'') for d in data])) for r in rank ]))for g in group])
    
    #this is the solution
    class dic2class(dict):
        def __init__(self, dic):
            for key,val in dic.items():
                self.__dict__[key]=self[key]=dic2class(val) if isinstance(val,dict) else val
    
    
    student_class=dic2class(student_dic)
    
    #one way to edit:
    student_class.class1.rank1['sex']='male'
    student_class.class1.rank1['name']='Nan Xiang'
    
    #two ways to query:
    print student_class.class1.rank1
    print student_class.class1['rank1']
    print '-'*50
    for rank in student_class.class1:
        print getattr(student_class.class1,rank)
    

    结果:

    {'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
    {'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
    --------------------------------------------------
    {'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
    {'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
    {'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
    {'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
    {'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
    
  • 43

    这也很有效

    class DObj(object):
        pass
    
    dobj = Dobj()
    dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}
    
    print dobj.a
    >>> aaa
    print dobj.b
    >>> bbb
    
  • 3
    class Struct(dict):
        def __getattr__(self, name):
            try:
                return self[name]
            except KeyError:
                raise AttributeError(name)
    
        def __setattr__(self, name, value):
            self[name] = value
    
        def copy(self):
            return Struct(dict.copy(self))
    

    用法:

    points = Struct(x=1, y=2)
    # Changing
    points['x'] = 2
    points.y = 1
    # Accessing
    points['x'], points.x, points.get('x') # 2 2 2
    points['y'], points.y, points.get('y') # 1 1 1
    # Accessing inexistent keys/attrs 
    points['z'] # KeyError: z
    points.z # AttributeError: z
    # Copying
    points_copy = points.copy()
    points.x = 2
    points_copy.x # 1
    
  • 29

    Build 我对“python: How to add property to a class dynamically?”的回答:

    class data(object):
        def __init__(self,*args,**argd):
            self.__dict__.update(dict(*args,**argd))
    
    def makedata(d):
        d2 = {}
        for n in d:
            d2[n] = trydata(d[n])
        return data(d2)
    
    def trydata(o):
        if isinstance(o,dict):
            return makedata(o)
        elif isinstance(o,list):
            return [trydata(i) for i in o]
        else:
            return o
    

    您在要转换的字典上调用 makedata ,或者根据您期望的输入调用 trydata ,它会吐出一个数据对象 .

    笔记:

    • 如果需要更多功能,可以将elif添加到 trydata .

    • 显然,如果你想要 x.a = {} 或类似的话,这将不起作用 .

    • 如果需要只读版本,请使用the original answer中的类数据 .

  • 27

    我有一些问题 __getattr__ 没有被调用所以我构建了一个新的样式类版本:

    class Struct(object):
        '''The recursive class for building and representing objects with.'''
        class NoneStruct(object):
            def __getattribute__(*args):
                return Struct.NoneStruct()
    
            def __eq__(self, obj):
                return obj == None
    
        def __init__(self, obj):
            for k, v in obj.iteritems():
                if isinstance(v, dict):
                    setattr(self, k, Struct(v))
                else:
                    setattr(self, k, v)
    
        def __getattribute__(*args):
            try:
                return object.__getattribute__(*args)
            except:            
                return Struct.NoneStruct()
    
        def __repr__(self):
            return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for 
    (k, v) in self.__dict__.iteritems()))
    

    此版本还添加了 NoneStruct ,在调用未设置的属性时返回 . 这允许None测试以查看属性是否存在 . 当确切的dict输入未知时(设置等)非常有用 .

    bla = Struct({'a':{'b':1}})
    print(bla.a.b)
    >> 1
    print(bla.a.c == None)
    >> True
    
  • 2

    我的字典是这种格式:

    addr_bk = {
        'person': [
            {'name': 'Andrew', 'id': 123, 'email': 'andrew@mailserver.com',
             'phone': [{'type': 2, 'number': '633311122'},
                       {'type': 0, 'number': '97788665'}]
            },
            {'name': 'Tom', 'id': 456,
             'phone': [{'type': 0, 'number': '91122334'}]}, 
            {'name': 'Jack', 'id': 7788, 'email': 'jack@gmail.com'}
        ]
    }
    

    可以看出,我有 nested dictionarieslist of dicts . 这是因为addr_bk是从使用lwpb.codec转换为python dict的协议缓冲区数据解码的 . 存在可选字段(例如,电子邮件=>其中密钥可能不可用)和重复字段(例如,电话=>转换为字典列表) .

    我尝试了以上提出的所有解决方案 . 有些不能很好地处理嵌套字典 . 其他人无法轻松打印对象细节 .

    只有Dawie Strauss的解决方案dict2obj(dict)效果最好 .

    当无法找到密钥时,我已经增强了一点处理:

    # Work the best, with nested dictionaries & lists! :)
    # Able to print out all items.
    class dict2obj_new(dict):
        def __init__(self, dict_):
            super(dict2obj_new, self).__init__(dict_)
            for key in self:
                item = self[key]
                if isinstance(item, list):
                    for idx, it in enumerate(item):
                        if isinstance(it, dict):
                            item[idx] = dict2obj_new(it)
                elif isinstance(item, dict):
                    self[key] = dict2obj_new(item)
    
        def __getattr__(self, key):
            # Enhanced to handle key not found.
            if self.has_key(key):
                return self[key]
            else:
                return None
    

    然后,我测试了它:

    # Testing...
    ab = dict2obj_new(addr_bk)
    
    for person in ab.person:
      print "Person ID:", person.id
      print "  Name:", person.name
      # Check if optional field is available before printing.
      if person.email:
        print "  E-mail address:", person.email
    
      # Check if optional field is available before printing.
      if person.phone:
        for phone_number in person.phone:
          if phone_number.type == codec.enums.PhoneType.MOBILE:
            print "  Mobile phone #:",
          elif phone_number.type == codec.enums.PhoneType.HOME:
            print "  Home phone #:",
          else:
            print "  Work phone #:",
          print phone_number.number
    

相关问题