Convert nested Python dict to object?
我正在寻找一种优雅的方法来获取数据,使用一些嵌套的dict和列表(即javascript样式的对象语法)对dict进行属性访问。
例如:
1 | >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo':"bar"}]} |
应该可以这样访问:
1 2 3 4 5 6 7 | >>> x = dict2obj(d) >>> x.a 1 >>> x.b.c 2 >>> x.d[1].foo bar |
我认为,如果没有递归,这是不可能的,但是有什么好方法可以获得dict的对象样式呢?
更新:在python 2.6及更高版本中,考虑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> 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'] |
备选方案(原始答案内容)是:
1 2 3 | class Struct: def __init__(self, **entries): self.__dict__.update(entries) |
然后,您可以使用:
1 2 3 4 5 6 7 8 | >>> args = {'a': 1, 'b': 2} >>> s = Struct(**args) >>> s <__main__.Struct instance at 0x01D6A738> >>> s.a 1 >>> s.b 2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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' |
令人惊讶的是,没有人提到过这群人。此库专门用于提供对dict对象的属性样式访问,并完全按照OP的要求执行。演示:
1 2 3 4 5 6 7 8 9 | >>> 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上找到-信贷转到codyzu
1 | x = type('new_dict', (object,), d) |
然后向这个添加递归,就完成了。
编辑这是我实现它的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | >>> 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' |
有一个收款助手叫
1 2 3 4 5 6 7 8 9 | 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 |
根据我的感受,这是前面例子中最好的方面,下面是我的想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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())) |
1 2 3 4 5 6 7 8 9 10 11 | 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 |
可用于任何深度的任何序列/dict/值结构。
如果您的dict来自
1 2 3 4 | import json from collections import namedtuple json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values())) |
另请参见如何将JSON数据转换为Python对象。
如果要将dict键作为对象(或作为困难键的dict)访问,请以递归方式进行,并且还可以更新原始dict,可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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) |
示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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'}} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | >>> 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' |
我最终尝试了attrdict和bundle库,发现它们对于我的使用来说太慢了。在我和一个朋友研究了它之后,我们发现编写这些库的主要方法导致库在一个嵌套的对象中急剧递归,并在整个过程中复制字典对象。考虑到这一点,我们做了两个关键的更改。1)我们将属性设置为lazy-loaded 2)而不是创建dictionary对象的副本,而是创建一个轻量级代理对象的副本。这是最终的实现。使用此代码的性能提高是令人难以置信的。当使用attrdict或bunch时,这两个库单独占用了我请求时间的1/2和1/3(什么!?)这段代码将时间缩短到几乎为零(在0.5毫秒的范围内)。当然,这取决于您的需求,但是如果您在代码中使用了这个功能,那么一定要使用类似这样的简单功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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-mericakel的原始实现。
另一件需要注意的是,这个实现非常简单,并且没有实现您可能需要的所有方法。您需要根据需要在dictproxy或listproxy对象上编写这些内容。
这应该让你开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
它还不适用于列表。您必须将列表包装在一个用户列表中,并重载
我知道这里已经有很多答案了,我迟到了,但是这个方法将递归地"就地"将字典转换成类似对象的结构…作品在3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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 |
我来解释一下我一段时间前几乎用过的解决方案。但首先,我没有这样做的原因是因为以下代码:
1 2 3 4 | d = {'from': 1} x = dict2obj(d) print x.from |
出现此错误:
1 2 3 4 | File"test.py", line 20 print x.from == 1 ^ SyntaxError: invalid syntax |
因为"from"是一个python关键字,所以您不能允许某些字典键。
现在,我的解决方案允许直接使用字典项的名称访问它们。但它也允许您使用"字典语义"。下面是示例用法的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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" |
您可以使用自定义对象挂钩来利用标准库的
1 2 3 4 5 6 7 8 | 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) |
示例用法:
1 2 3 4 5 6 7 8 9 10 | >>> 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' |
而且它不是严格的只读的,因为它与
1 2 3 | >>> o.b.c = 3 >>> o.b.c 3 |
老问答,但我有更多的话要说。似乎没人谈论递归dict。这是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | #!/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() |
想上传我的版本。
1 2 3 4 5 6 7 8 9 | 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) |
它保留导入到类中的类型的属性。我唯一关心的是从字典中重写您解析的方法。但其他方面似乎是坚实的!
1 2 3 4 5 6 | from mock import Mock d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo':"bar"}]} my_data = Mock(**d) # We got # my_data.a == 1 |
把你的
1 2 3 4 5 6 7 8 9 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 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 |
您的示例list元素可能是一个
1 2 3 4 | >>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo':"bar"})]} >>> obj = dict2obj(d) >>> obj.d.hi.foo "bar" |
我偶然发现了一个需要递归地将dict列表转换为对象列表的情况,因此基于罗伯托的片段,这里为我做了什么工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
请注意,由于明显的原因,任何元组都将被转换为等价的列表。
希望这能像你们对我的回答一样帮助别人,伙计们。
这也很有效
1 2 3 4 5 6 7 8 9 10 | class DObj(object): pass dobj = Dobj() dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'} print dobj.a >>> aaa print dobj.b >>> bbb |
下面是另一个实现:
1 2 3 4 5 6 7 8 | 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())) |
[编辑]关于处理列表中的听写而不仅仅是其他听写的遗漏。添加修复。
我认为听写大多数时候都是由数字、字符串和听写组成的。所以我忽略了tuples、list和其他类型不出现在dict的最终维中的情况。
考虑到继承,结合递归,它方便地解决了打印问题,并且提供了两种查询数据的方法,一种是编辑数据的方法。
请参阅下面的示例,描述有关学生的一些信息的dict:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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) |
结果:
1 2 3 4 5 6 7 8 | {'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': ''} |
这个怎么样?
1 2 | from functools import partial d2o=partial(type,"d2o", ()) |
然后可以这样使用:
1 2 3 4 5 | >>> o=d2o({"a" : 5,"b" : 3}) >>> print o.a 5 >>> print o.b 3 |
以下是实现SilentGhost原始建议的另一种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
基于我对"python:如何动态地向类添加属性"的回答?:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
您可以在要转换的字典上调用
笔记:
- 如果需要更多的功能,可以将elifs添加到
trydata 中。 - 很明显,如果你想要
x.a = {} 或类似的产品,这是行不通的。 - 如果需要只读版本,请使用原始答案中的类数据。
1 2 3 4 5 6 7 8 9 10 11 12 | 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)) |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
下面是一个名为duple的嵌套就绪版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from collections import namedtuple class Struct(object): def __new__(cls, data): if isinstance(data, dict): return namedtuple( 'Struct', data.iterkeys() )( *(Struct(val) for val in data.values()) ) elif isinstance(data, (tuple, list, set, frozenset)): return type(data)(Struct(_) for _ in data) else: return data |
= >
1 2 3 4 5 6 7 8 | >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo':"bar"}]} >>> s = Struct(d) >>> s.d ['hi', Struct(foo='bar')] >>> s.d[0] 'hi' >>> s.d[1].foo 'bar' |
我的字典是这样的格式:
1 2 3 4 5 6 7 8 9 10 11 | addr_bk = { 'person': [ {'name': 'Andrew', 'id': 123, 'email': '[email protected]', 'phone': [{'type': 2, 'number': '633311122'}, {'type': 0, 'number': '97788665'}] }, {'name': 'Tom', 'id': 456, 'phone': [{'type': 0, 'number': '91122334'}]}, {'name': 'Jack', 'id': 7788, 'email': '[email protected]'} ] } |
可以看到,我有嵌套字典和字典列表。这是因为addr_bk是从使用lwpb.codec转换为python dict的协议缓冲区数据中解码出来的。有可选字段(例如,email=>其中键可能不可用)和重复字段(例如,phone=>转换为dict列表)。
我尝试了上述所有的解决方案。有些词典处理嵌套词典不好。其他人无法轻松打印对象详细信息。
只有DawieStrauss的dict2obj(dict)解决方案最有效。
当找不到钥匙时,我对它进行了一些改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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 |
然后,我测试了它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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 |
这是将字典列表转换为对象的另一种可选方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def dict2object(in_dict): class Struct(object): def __init__(self, in_dict): for key, value in in_dict.items(): if isinstance(value, (list, tuple)): setattr( self, key, [Struct(sub_dict) if isinstance(sub_dict, dict) else sub_dict for sub_dict in value]) else: setattr( self, key, Struct(value) if isinstance(value, dict) else value) return [Struct(sub_dict) for sub_dict in in_dict] \ if isinstance(in_dict, list) else Struct(in_dict) |
这个小类从不给我任何问题,只需扩展它并使用copy()方法:
1 2 3 4 5 6 7 8 9 | import simplejson as json class BlindCopy(object): def copy(self, json_str): dic = json.loads(json_str) for k, v in dic.iteritems(): if hasattr(self, k): setattr(self, k, v); |
我对没有被调用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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())) |
此版本还添加了
1 2 3 4 5 | bla = Struct({'a':{'b':1}}) print(bla.a.b) >> 1 print(bla.a.c == None) >> True |