我正在寻找一种优雅的方法,使用一些嵌套的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的对象样式的好方法?
更新:在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 "", line 1, in 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
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'
令人惊讶的是没有人提到过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'
可以在https://github.com/Infinidat/munch上找到一个Python 3库- Credit转到codyzu
x = type('new_dict', (object,), d)
然后添加递归到这个,你就完成了.
编辑这是我实现它的方式:
>>> 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'
有一个名为的集合帮助器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(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
可以与任何深度的任何序列/字典/值结构一起使用.
我认为这是前面例子中最好的方面,这就是我想出的:
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()))
如果你的dict来自json.loads()
,你可以把它变成一个对象(而不是一个dict)在一行:
import json from collections import namedtuple json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
另请参见如何将JSON数据转换为Python对象.
如果你想要将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'}}
>>> 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和束库,并发现它们对我的使用来说太慢了.在我和朋友调查之后,我们发现编写这些库的主要方法导致库通过嵌套对象进行主动递归,并在整个过程中复制字典对象.考虑到这一点,我们做了两个关键的改变.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
请通过/sf/ask/17360801/查看此处的原始实施.
另外需要注意的是,这个实现非常简单,并没有实现您可能需要的所有方法.您需要在DictProxy或ListProxy对象上根据需要编写它们.
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 x.a print x.b.c print x.d[1].foo
它不适用于列表.您必须将列表包装在UserList中并重载__getitem__
以包装dicts.
我知道这里已经有很多答案了,我参加聚会很晚,但是这种方法将递归地将“字典”转换成类似对象的结构...在3.xx中有效
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