如何将嵌套的Python字典转换为对象?

我正在寻找一种优雅的方式来获得数据使用属性访问字典与一些嵌套的字典和列表(即javascript风格的对象语法)。

例如:

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

我想,如果没有递归,这是不可能的,但是有什么更好的方法来获得字典的对象样式呢?

416316 次浏览
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'

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__来包装字典。

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

让我来解释一下我以前用过的一个解决方案。但首先,我没有这样做的原因可以通过以下代码来说明:

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"

在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
class obj(object):
def __init__(self, d):
for k, v in d.items():
if isinstance(k, (list, tuple)):
setattr(self, k, [obj(x) if isinstance(x, dict) else x for x in v])
else:
setattr(self, k, obj(v) if isinstance(v, dict) else v)


>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = obj(d)
>>> x.b.c
2
>>> 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()))

[编辑]遗漏了在列表中处理字典的部分,而不仅仅是其他字典。添加修复。

下面是执行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

根据我对“如何动态地向类添加属性?”的回答:

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,它将输出一个数据对象。

注:

  • 如果需要更多功能,可以向trydata添加elif。
  • 显然,如果你想要x.a = {}或类似的东西,这是行不通的。
  • 如果您想要一个只读版本,请使用原来的答案中的类数据。

我偶然发现的情况下,我需要递归转换字典列表到对象列表,所以基于罗伯托的片段在这里为我做了什么工作:

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:
"""The recursive class for building and representing objects with."""


def __init__(self, obj):
for k, v in obj.items():
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__.items()))
# Applies to Python-3 Standard Library
class Struct(object):
def __init__(self, data):
for name, value in data.items():
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




# Applies to Python-2 Standard Library
class Struct(object):
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

可以用于任何深度的任何序列/字典/值结构。

< p >有一个

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

老问题了,但我还有话要说。似乎没有人谈论递归字典。这是我的代码:

#!/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()
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

我想上传我对这个小范例的看法。

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)

它保留导入到类中的类型的属性。我唯一关心的是从解析的字典中覆盖方法。但其他方面似乎很可靠!

我有一些问题,__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测试来查看属性是否存在。非常有用时,确切的字典输入是不知道的(设置等)。

bla = Struct({'a':{'b':1}})
print(bla.a.b)
>> 1
print(bla.a.c == 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 dict的协议缓冲区数据解码的。有可选字段(例如email =>,其中键可能不可用)和重复字段(例如phone =>转换为dict列表)

我尝试了上述所有建议的解决方案。有些不能很好地处理嵌套字典。其他的则不容易打印对象的详细信息。

只有Dawie Strauss的dic2obj (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

如果你想访问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'}}

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

import json
from collections import namedtuple


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

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

这个怎么样:

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

然后可以这样使用:

>>> o=d2o({"a" : 5, "b" : 3})
>>> print o.a
5
>>> print o.b
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
我认为字典由数字、字符串和字典组成,大多数时候就足够了。 所以我忽略了元组、列表和其他类型没有出现在字典的最后一个维度的情况。< / p >

考虑了继承,结合递归,方便地解决了打印问题,并提供了两种数据查询方式,一种数据编辑方式。

请看下面的例子,这是一个描述学生信息的词典:

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

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

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)

令人惊讶的是,没有人提到。这个库专门用于提供对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到codyzu

>>> from munch import DefaultMunch
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> obj = DefaultMunch.fromDict(d)
>>> obj.b.c
2
>>> obj.a
1
>>> obj.d[1].foo
'bar'

这个小类从来没有给我任何问题,只是扩展它并使用copy()方法:

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

我最终尝试了AttrDict库,发现它们对我的使用来说太慢了。在我和一个朋友深入研究之后,我们发现编写这些库的主要方法是在库中积极递归地遍历嵌套对象,并在整个过程中复制字典对象。考虑到这一点,我们做了两个关键更改。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

查看原始实现在这里 by https://stackoverflow.com/users/704327/michael-merickel

另一件需要注意的事情是,这个实现非常简单,并且没有实现您可能需要的所有方法。您需要根据需要在DictProxy或ListProxy对象上写入这些内容。

下面是一个使用namedtuple的嵌套就绪版本:

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

= >

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

你可以利用标准库的# EYZ0模块自定义对象钩子:

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'

它是不是严格的只读,因为它是namedtuple,即你可以改变值-而不是结构:

>>> o.b.c = 3
>>> o.b.c
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
>>> 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"

这也很有效

class DObj(object):
pass


dobj = Dobj()
dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}


print dobj.a
>>> aaa
print dobj.b
>>> bbb

我知道这里已经有很多答案了,我迟到了,但这个方法将递归和“就地”将字典转换为类对象结构……适用于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

dict转换为object

from types import SimpleNamespace


def dict2obj(data):
"""将字典对象转换为可访问的对象属性"""
if not isinstance(data, dict):
raise ValueError('data must be dict object.')


def _d2o(d):
_d = {}
for key, item in d.items():
if isinstance(item, dict):
_d[key] = _d2o(item)
else:
_d[key] = item
return SimpleNamespace(**_d)


return _d2o(data)

参考答案 .

通常情况下,您希望将字典层次结构镜像到对象中,而不是列表或元组,它们通常处于最低级别。我是这样做的:

class defDictToObject(object):


def __init__(self, myDict):
for key, value in myDict.items():
if type(value) == dict:
setattr(self, key, defDictToObject(value))
else:
setattr(self, key, value)

所以我们这样做:

myDict = { 'a': 1,
'b': {
'b1': {'x': 1,
'y': 2} },
'c': ['hi', 'bar']
}

并获得:

# EYZ0 1

x.c ['hi', 'bar']

我对标记和点赞的答案不满意,所以这里有一个简单一般解决方案,用于将json风格的嵌套数据结构(由字典和列表组成)转换为普通对象的层次结构:

# tested in: Python 3.8
from collections import abc
from typings import Any, Iterable, Mapping, Union


class DataObject:
def __repr__(self):
return str({k: v for k, v in vars(self).items()})


def data_to_object(data: Union[Mapping[str, Any], Iterable]) -> object:
"""
Example
-------
>>> data = {
...     "name": "Bob Howard",
...     "positions": [{"department": "ER", "manager_id": 13}],
... }
... data_to_object(data).positions[0].manager_id
13
"""
if isinstance(data, abc.Mapping):
r = DataObject()
for k, v in data.items():
if type(v) is dict or type(v) is list:
setattr(r, k, data_to_object(v))
else:
setattr(r, k, v)
return r
elif isinstance(data, abc.Iterable):
return [data_to_object(e) for e in data]
else:
return data

最简单的方法是使用collections.namedtuple

我发现下面的4行代码是最漂亮的,它支持嵌套字典:

def dict_to_namedtuple(typename, data):
return namedtuple(typename, data.keys())(
*(dict_to_namedtuple(typename + '_' + k, v) if isinstance(v, dict) else v for k, v in data.items())
)

输出看起来也会很好:

>>> nt = dict_to_namedtuple('config', {
...     'path': '/app',
...     'debug': {'level': 'error', 'stream': 'stdout'}
... })


>>> print(nt)
config(path='/app', debug=config_debug(level='error', stream='stdout'))


>>> print(nt.debug.level)
'error'

如果你想让它递归的话,在之前接受的答案所做的基础上。

class FullStruct:
def __init__(self, **kwargs):
for key, value in kwargs.items():
if isinstance(value, dict):
f = FullStruct(**value)
self.__dict__.update({key: f})
else:
self.__dict__.update({key: value})

在@max-sirwa的代码上更新了递归数组展开

class Objectify:
def __init__(self, **kwargs):
for key, value in kwargs.items():
if isinstance(value, dict):
f = Objectify(**value)
self.__dict__.update({key: f})
elif isinstance(value, list):
t = []
for i in value:
t.append(Objectify(**i)) if isinstance(i, dict) else t.append(i)
self.__dict__.update({key: t})
else:
self.__dict__.update({key: value})

在2021年,使用pydantic BaseModel -将嵌套字典和嵌套json对象转换为python对象,反之亦然:

< a href = " https://pydantic-docs.helpmanual。io /使用/模型/ noreferrer“rel = > https://pydantic-docs.helpmanual.io/usage/models/ < / >

>>> class Foo(BaseModel):
...     count: int
...     size: float = None
...
>>>
>>> class Bar(BaseModel):
...     apple = 'x'
...     banana = 'y'
...
>>>
>>> class Spam(BaseModel):
...     foo: Foo
...     bars: List[Bar]
...
>>>
>>> m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])

对象to dict

>>> print(m.dict())
{'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}]}

对象转换为JSON

>>> print(m.json())
{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}

反对的词典

>>> spam = Spam.parse_obj({'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y2'}]})
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y2')])

JSON到对象

>>> spam = Spam.parse_raw('{"foo": {"count": 4, "size": null}, "bars": [{"apple": "x1", "banana": "y"}, {"apple": "x2", "banana": "y"}]}')
>>> spam
Spam(foo=Foo(count=4, size=None), bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')])
class Dict2Obj:
def __init__(self, json_data):
self.convert(json_data)


def convert(self, json_data):
if not isinstance(json_data, dict):
return
for key in json_data:
if not isinstance(json_data[key], dict):
self.__dict__.update({key: json_data[key]})
else:
self.__dict__.update({ key: Dict2Obj(json_data[key])})

我找不到嵌套字典到对象的实现,所以写了一个。

用法:

>>> json_data = {"a": {"b": 2}, "c": 3}
>>> out_obj = Dict2Obj(json_data)
>>> out_obj.a
<Dict2Obj object at 0x7f3dc22c2d68>
>>> out_obj.a.b
2
>>> out_obj.a.c
3

你可以用我的方法来处理。

somedict= {"person": {"name": "daniel"}}


class convertor:
def __init__(self, dic: dict) -> object:
self.dict = dic


def recursive_check(obj):
for key, value in dic.items():
if isinstance(value, dict):
value= convertor(value)
setattr(obj, key, value)
recursive_check(self)
my_object= convertor(somedict)


print(my_object.person.name)

dict寻找一个简单的包装器类,支持属性样式的键访问/赋值(点符号),我对现有选项不满意,原因如下。

dataclassespydantic等等都很棒,但是需要一个静态的内容定义。此外,它们不能在依赖于dict的代码中取代dict,因为它们不共享相同的方法,而且不支持__getitem__()语法。

因此,我开发了MetaDict。它的行为与dict完全相同,但是支持点表示法和IDE自动补全(如果对象被加载到RAM中),而没有其他解决方案的缺点和潜在的名称空间冲突。所有功能和使用示例都可以在GitHub上找到(见上面的链接)。

充分披露:我是MetaDict的作者。

我在尝试其他解决方案时遇到的缺点/限制:

  • 瘾君子
    • IDE中没有键自动补全
    • 嵌套键分配不能关闭
    • 新分配的dict对象不会转换为支持属性样式键访问
    • 阴影内置类型Dict
  • 产品
    • 不定义静态模式(类似于dataclass), IDE中没有键自动补全功能
    • 当嵌入list或其他内置可迭代对象时,不进行dict对象的递归转换
  • AttrDict
    • IDE中没有键自动补全
    • 在后台将list对象转换为tuple对象
  • Munch
    • 内置方法,如items()update()等,可以用obj.items = [1, 2, 3]覆盖
    • 当嵌入list或其他内置可迭代对象时,不进行dict对象的递归转换
  • EasyDict
    • 只有字符串是有效的键,但是dict接受所有可哈希对象作为键
    • 内置方法,如items()update()等,可以用obj.items = [1, 2, 3]覆盖
    • 内置方法的行为不像预期的那样:obj.pop('unknown_key', None)引发AttributeError

注意:我在stackoverflow中写了一个类似的答案,这是相关的。