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


537

我正在寻找一种优雅的方式来获取数据,该数据使用具有一些嵌套的字典和列表的字典的属性访问(即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

我认为,没有递归是不可能的,但是获得字典对象样式的一种好方法是什么?


6
我最近试图做类似的事情,但是一个重复的字典键(“ from”-一个Python关键字)使我无法通过它。因为一旦您尝试使用“ x.from”来访问该属性,就会得到语法错误。
Dawie Strauss,2009年

3
确实这是一个问题,但是我可以放弃“ from”,使访问大型dict构造变得更轻松:)键入x ['a'] ['d'] [1] ['foo']确实很烦人,所以xad [1] .foo规则。如果需要from,则可以通过getattr(x,'from')进行访问,也可以使用_from作为属性。
马克

5
from_而不是_from根据PEP 8
科斯(Kos)

1
您可以使用getattr(x, 'from')而不是重命名该属性。
乔治五世·赖利

3
这些“解决方案”中的大多数似乎都不起作用(即使是公认的解决方案,也不允许嵌套d1.b.c),我认为很明显,您应该使用库中的某些东西,例如集合中的namedtuple,如答案所示,.. 。
安迪·海登

Answers:


659

更新:在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

19
此处相同-这对于从面向文档的数据库(如MongoDB)重建Python对象特别有用。
mikemaccana

13
要获得更漂亮的打印效果,请添加:def repr __(self):返回'<%s>'%str('\ n'.join('%s:%s'%(k,repr(v))表示(k,v )中的self .__ dict .iteritems()))
six8 2011年

15
嵌套字典可以使用吗?以及包含对象和/或列表等的字典。是否有捕获?
山姆·斯托林加

5
@Sam S:它不会Struct从嵌套字典创建nested ,但是通常值的类型可以是任何东西。密钥仅限于适用于对象插槽
Eli Bendersky

51
-1,因为一)它不工作嵌套类型的字典,这问题问清楚了,和b)相同的功能目前存在于标准库argparse.Namespace(这也定义__eq____ne____contains__)。
2013年

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

7
好!我会用.iteritems()替换.items(),以减少内存占用。
Eric O Lebigot 09年

7
如果不是OP要求,那么这不是问题-但请注意,这不会递归处理列表中列表中的对象。
匿名2009年

3
我意识到这是一个古老的答案,但是现在最好使用抽象基类而不是丑陋的线条if isinstance(b, (list, tuple)):
wim 2013年

4
提示:让obj类继承自argparse.Namespace其他功能,例如可读的字符串表示形式。
塞拉诺2014年

2
根据不同的使用情况下,通常我们会检查它是不是一个字符串类型,但它一个序列类型
维姆

107

令人惊讶的是,没有人提到邦奇。该库专门用于提供对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库- 版权归codyzu所有


16
并且有一个名为Bunch的python 3兼容(看来是2.6-3.4)分支:github.com/Infinidat/munch
codyzu 2014年

Bunch是所有这些中的最佳解决方案,因为它很好地支持多种类型的序列化并得以维护。太好了谢谢。我希望python3版本的名称相同,因为为什么不这样。
乔纳森·

4
因此,就此而言,预警,Bunch和Attrdict都很慢。看到他们经常在我们的应用程序中使用时,他们分别消耗了我大约1/3和1/2的请求时间。绝对不是要忽略的东西。讨论更多关于stackoverflow.com/a/31569634/364604的信息
JayD3e 2015年

尽管这确实允许类对象访问字典,但它通过getattr这样做。这使得像IPython这样的智能REPL难以自省。
GDorn

很好奇这与JSONpath相比github.com/kennknowles/python-jsonpath-rw
MarkHu

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

1
为什么要创建类型对象而不实例化它们?那不是更合逻辑吗?我的意思是,为什么不这样做top_instance = top(),然后在返回的地方返回top
pancake

6
很适合“叶子”数据,但是这些示例方便地省略了像“树枝” x并且x.b返回丑陋的位置<class '__main__.new'>
MarkHu 2014年

46

有一个名为的收集助手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

28
这不能回答嵌套字典的递归问题。
2013年

4
您不能修改namedtuple。
Max

我们是否可以保证keys()和values()返回的列表的顺序匹配?我的意思是,如果它是OrderedDict,是的。但是标准的命令?我知道CPython 3.6+和PyPy“紧凑”命令是有序的,但是引用了文档:“此新实现的顺序保留方面被认为是实现细节,不应依赖”
Havok

38
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

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


4
这应该是答案。它非常适合嵌套。您也可以将其用作json.load()的object_hook。
010110110101

2
与2009年SilentGhost的功能性答案类似,叶节点数据是可访问的,但父级/细枝将显示为对象引用。要进行漂亮印刷,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu 2014年

5
Python 3.x用户:.items()而不是.iteritems()第4行。(该函数已重命名,但本质上相同
neo post modern

非常适合嵌套对象,谢谢!作为一个新手评论我,为python3 iteritems()必须改变,以项目()
奥斯卡·安德鲁

31

以我认为是前面示例的最佳方面,这是我想到的:

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

请注意,构造函数可以简化为: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) 这也删除了对Struct
George V. Reilly

2
我宁愿不要低估我自己的答案,但回顾这一点,我注意到它并没有递归到序列类型中。xd [1] .foo在这种情况下失败。
andyvanee

2
isinstance(v, dict)检查会更好,因为isinstance(v, collections.Mapping)这样它可以处理未来类似字典的东西
灶台

29

如果您的字典来自json.loads(),则可以一行将其变成一个对象(而不是字典):

import json
from collections import namedtuple

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

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


.loads如果您有一个巨大的JSON对象,似乎比常规的要慢得多
michaelsnowden

16

如果要将字典键作为对象(或作为困难键的字典)访问,请递归地进行操作,并且还能够更新原始字典,则可以执行以下操作:

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

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

12

我最终都尝试了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查看此处的原始实现。

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


9

x.__dict__.update(d) 应该做的很好。


感谢您的回答,但是x是多少?字典或标准对象?请给我一个提示。
马克

x是您的对象。每个对象都有一个字典。通过更新对象的字典,您实际上是在更新其中的键变量。
亚历克斯·罗德里格斯

粗体字是_ _ dict _ _
Alex Rodrigues

15
这不会处理嵌套字典。
2009年

并非每个对象都具有__dict__:尝试object().__dict__即可获得AttributeError: 'object' object has no attribute '__dict__'
Will Manley

8

您可以通过自定义对象挂钩来利用标准库的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)
>>> 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

1
迄今为止的最佳答案
Connor

我喜欢将json加载机制用于嵌套元素的想法。但是我们已经有了一个字典,我不喜欢这样一个事实,即需要从中创建一个字符串以将其映射到一个对象中。我希望有一个直接从字典创建对象的解决方案。
桑德罗

1
需要先转换为字符串,但相当通用,因为可以通过json.JSONEncoder和扩展它object_hook
桑德罗

6

这应该使您开始:

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


2
要使其适用于列表,请使用的答案中的if isinstance(d, list)子句Anon
Vinay Sajip,2009年

6

我知道这里已经有很多答案了,我参加聚会很晚,但是这种方法将递归地将“字典”转换成类似对象的结构...在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

什么是“类对象结构”?
MoralCode '18

1
因为鸭子的打字原理,所以像对象。我的意思是,您可以像访问类的实例一样访问其属性。但这不是一个对象,而是一个命名的元组。
艾丹·哈登·赖特

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

# We got
# my_data.a == 1

4

让我解释一下我的解决方案几乎使用前一段时间。但是首先,以下代码说明了我没有这样做的原因:

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"

1
类结构:def init __(self,** entries):self .__ dict .update(entries)
Kenneth Reitz 2010年

4

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

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

4

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

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)

它保留导入到类中的类型的属性。我唯一关心的是从解析的字典中覆盖方法。但是否则看起来很稳固!


尽管这是一个不错的主意,但这似乎不适用于OP的示例,实际上,它似乎修改了传入的字典!实际上,df.a它甚至都不起作用。
安迪·海登

4

这也很好

class DObj(object):
    pass

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

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

3

这是实现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

3

我偶然发现需要递归将字典列表转换为对象列表的情况,因此根据罗伯托的代码段,这里为我做了什么工作:

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

注意,出于明显的原因,任何元组都将转换为其等效列表。

希望这对某人有帮助,就像您为我所做的所有回答一样。


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

您的示例list元素可能应该是Mapping,是(键,值)对的列表,如下所示:

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

[编辑]关于还处理列表中的命令,而不仅仅是其他命令的遗漏之处。添加了修复程序。


请注意,将dict设置为源字典意味着对结果对象的属性进行的任何更改也会影响创建该对象的字典,反之亦然。如果将字典用于创建对象以外的其他用途,则可能导致意外结果。
Mark Roddy

@Mark:实际上,每次都会有一个新的字典传递给DictObj,而不仅仅是传递相同的dict对象,因此这实际上不会发生。这样做是必要的,因为我还需要翻译字典中的,因此,如果不对自身进行变异就无法通过原始dict对象。
布赖恩

有很多答案,被接受的答案似乎不是递归的或处理列表。我通读了所有内容,这看起来乍一看是最简单的,我进行了测试并成功了。好答案。
AlwaysTraining

2
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

2

这个怎么样:

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

然后可以这样使用:

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

2

我认为一个字典由数字,字符串和字典组成,大多数时候就足够了。因此,我忽略了元组,列表和其他类型未出现在字典最终维度中的情况。

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

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

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

2

通常,您想将dict层次结构镜像到您的对象中,而不是通常位于最低级别的列表或元组。所以这就是我这样做的方式:

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

并获得:

x.b.b1.x 1个

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


1

我的字典是这样的格式:

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的协议缓冲区数据中解码的。有可选字段(例如,电子邮件=>,其中的密钥可能不可用)和重复字段(例如,电话=>转换为词典列表)。

我尝试了以上所有建议的解决方案。有些不能很好地处理嵌套字典。其他人无法轻松打印对象详细信息。

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

1

建立我对“ python:如何动态地向类添加属性? ”的答案:

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 = {}或类似的方法将无法使用。
  • 如果需要只读版本,请使用原始答案中的类数据。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.