将Python字典转换为对象?

我正在寻找一个优雅的方式来转换一个正常的Python字典与一些嵌套的字典到一个对象。

例如:

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

应该可以这样访问:

 >>> x = dict2obj(d) >>> xa 1 >>> xbc 2 >>> xd[1].foo bar 

我认为,这是不可能没有recursion,但什么是一个很好的方式来获得一个对象的风格的字典?

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

 >>> from collections import namedtuple >>> MyStruct = namedtuple('MyStruct', 'ab d') >>> s = MyStruct(a=1, b={'c': 2}, d=['hi']) >>> s MyStruct(a=1, b={'c': 2}, d=['hi']) >>> sa 1 >>> sb {'c': 2} >>> sc Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyStruct' object has no attribute 'c' >>> sd ['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> >>> sa 1 >>> sb 2 
 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) >>> xbc 2 >>> xd[1].foo 'bar' 

令人惊讶的是,没有人提到过Bunch 。 这个库专门用来提供字典对象的属性风格访问,并且完全是OP想要的。 示范:

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

一个Python 3库可以在https://github.com/Infinidat/munch获得; – 信用评价是codyzu

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

然后添加recursion到这个,你就完成了。

编辑这是我如何实现它:

 >>> 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) >>> xa 1 >>> xbc 2 >>> xd[1].foo 'bar' 

对于现在偶然碰到这个问题的任何人。 在Python 2.6+中,有一个名为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 

以我感觉的是前面例子的最好的方面,下面是我想到的:

 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())) 
 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 

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

如果你的字典是来自json.loads() ,你可以把它变成一个对象而不是一个字典在一行:

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

另请参见如何将JSON数据转换为Python对象 。

如果你想访问字典键作为一个对象(或作为困难的键字典),recursion地做,也能够更新原始的字典,你可以这样做:

 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.ef = 'g' assert dd['e'].f == 'g' assert d == {'c': 'd', 'e': {'f': 'g'}} 
 >>> 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) >>> xa 1 >>> xbc 2 >>> xd[1].foo 'bar' 

x.__dict__.update(d)应该可以。

这应该让你开始:

 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 xa print xbc print xd[1].foo 

它不适用于列表。 你必须将列表包装在UserList中,并重载__getitem__来包装字典。

我最终尝试了AttrDict和Bunch库,发现它们对于我的使用来说太慢了。 在朋友和我研究过之后,我们发现编写这些库的主要方法会导致库通过嵌套对象进行积极的recursion,并在整个过程中复制字典对象。 考虑到这一点,我们做了两个关键的改变。 1)我们使属性延迟加载2)而不是创build一个字典对象的副本,我们创build一个轻量级代理对象的副本。 这是最后的实施。 使用此代码的性能提高令人难以置信。 当使用AttrDict或Bunch时,这两个库单独使用我的请求时间的1/2和1/3(什么!?)。 这段代码将时间缩短到几乎没有(0.5毫秒范围内的某个地方)。 这当然取决于你的需求,但是如果你在你的代码中使用了这个function,肯定要用这样简单的东西。

 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查看原始实施。;

另外要注意的是,这个实现非常简单,并没有实现你可能需要的所有方法。 您需要按照DictProxy或ListProxy对象的要求编写这些代码。

老问答,但我有更多的话要说。 似乎没有人谈论recursion字典。 这是我的代码:

 #!/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 xa == x["a"] == 1 assert xbc == x["b"]["c"] == 2 assert xbd[0] == 3 assert xbd[1] == 4 assert xbe[0][0] == 6 assert xbe[1][0] == 8 assert x[1] == 10 assert x["1"] == 11 assert x[1] != x["1"] assert id(x) == id(xbself.b.self) == id(xbself) assert xbself.a == xbself.b.self.a == 1 xx = 12 assert xx == x["x"] == 12 xy = {"a":13,"b":[14,15]} assert xya == 13 assert xyb[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 xac assert 3 in xac assert 5 in xac assert 1 in xab assert 4 in xab 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 xab assert 17 in xac if __name__ == '__main__': test01() test02() 

我偶然发现了我需要recursion转换列表中的对象列表的情况下,所以根据罗伯托的片段在这里什么工作对我来说:

 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 

请注意,任何元组都将被转换为等价的列表,原因很明显。

希望这可以帮助一个人,就像你为我所做的所有答案一样,伙计们。

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

 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) 

它保留了导入到类中的types的属性。 我唯一担心的就是从parsing的字典中覆盖方法。 但是,否则似乎坚实!

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

您可以利用标准库的json模块自定义对象钩子

 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) >>> oa 1 >>> obc 2 >>> od[0] u'hi' >>> od[1].foo u'bar' 

不是严格只读,因为它是与namedtuple ,即您可以更改值 – 不是结构:

 >>> obc = 3 >>> obc 3 

那么把你的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 >>> da 1 """ pass 

当然,除非你recursion地使用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.bc 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" 

这是实现SilentGhost原始build议的另一种方式:

 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 

让我解释一下我以前几乎用过的解决scheme。 但首先,我没有这样做的原因是由以下代码来说明的:

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

给出这个错误:

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

由于“from”是一个Python关键字,因此您不能使用某些字典键。


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

 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 xa == x['a'] == 1 assert xbc == x['b']['c'] == 2 assert xd[1].foo == x['d'][1]['foo'] == "bar" 

这是另一个实现:

 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())) 

错过了一些关于处理列表中的string,而不是其他字典。 增加了修复。

这个怎么样:

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

这可以像这样使用:

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

我觉得一个字典是由数字,string和字典组成的,大部分时间是足够的。 所以我忽略了元组,列表和其他types不出现在字典的最后维度的情况。

考虑到inheritance问题,结合recursion方法,可以方便地解决打印问题,同时提供了两种查询数据的方式,一种是编辑数据的方式。

看下面的例子,描述一些关于学生的信息:

 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': ''} 

这也可以

 class DObj(object): pass dobj = Dobj() dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'} print dobj.a >>> aaa print dobj.b >>> bbb 
 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 

build立我的答案“ python:如何dynamic地添加属性到一个类? ”:

 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在你想转换的字典上调用makedata ,或者根据你期望的input调用makedata ,并且吐出一个数据对象。

笔记:

  • 如果你需要更多的function,你可以添加elifs到trydata
  • 显然,如果你想要xa = {}或类似的话,这将不起作用。
  • 如果您需要只读版本,请使用原始答案中的类别数据。

我有一些问题__getattr__没有被调用,所以我构build了一个新的风格类版本:

 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 。 这允许Nonetesting来查看属性是否存在。 当确切的字典input不知道(设置等)非常有用。

 bla = Struct({'a':{'b':1}}) print(bla.ab) >> 1 print(bla.ac == None) >> True 

我的字典是这样的格式:

 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'} ] } 

可以看出,我嵌套了词典词典 列表 。 这是因为addr_bk是从使用lwpb.codec转换为python字典的协议缓冲区数据解码的。 有可选的字段(例如电子邮件=>其中可能不可用键)和重复的字段(例如电话=>转换为字典的列表)。

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

只有Dawie Strauss的dict2obj(dict)才是最好的解决scheme。

无法find密钥时,我已经加强了一点处理:

 # 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了它:

 # 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 

这是将字典列表转换为对象的另一种替代方法:

 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)