当前位置:  开发笔记 > 编程语言 > 正文

将嵌套的Python dict转换为对象?

如何解决《将嵌套的Pythondict转换为对象?》经验,为你挑选了14个好方法。

我正在寻找一种优雅的方法,使用一些嵌套的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的对象样式的好方法?



1> Eli Bendersk..:

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


-1因为a)它不适用于嵌套的dicts,这个问题明确要求,以及b)`argparse.Namespace`中的标准库中当前存在相同的功能(它还定义了`__eq__`,`__ne__` ,`__contains__`).
同样在这里 - 这对于从MongoDB等面向文档的数据库重建Python对象特别有用.
这适用于嵌套字典吗?和dicts包含对象和/或列表等.是否有任何捕获?
要获得更漂亮的打印,请添加:def __repr __(self):返回'<%s>'%str('\n'.join('%s:%s'%(k,repr(v))for(k,v) )in self .__ dict __.iteritems()))
@Sam S:它不会从嵌套字典创建嵌套的`struct`s,但通常值的类型可以是任何东西.密钥仅限于适用于对象槽
你有嵌套词典的解决方案吗?

2> Nadia Alraml..:
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'


如果不是OP要求,这不是问题 - 但请注意,这不会递归地处理列表中列表中的对象.
好!不过,我会用.iteritems()替换.items(),以减少内存占用.
提示:让`obj`类继承自`argparse.Namespace`以获取可读字符串表示等其他功能.
我意识到这是一个古老的答案,但是现在最好使用[抽象基类](http://docs.python.org/2/library/abc.html)而不是那个丑陋的行`if isinstance (b,(list,tuple)):`
根据用例,通常我们会检查它是_not_一个字符串类型,但它_is_是一个序列类型

3> kontinuity..:

令人惊讶的是没有人提到过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


还有一个python 3兼容(似乎是2.6-3.4)束名为Munch的分支:https://github.com/Infinidat/munch
所以只是预警,Bunch和Attrdict以及这件事情都非常缓慢.当他们在我们的应用程序中经常使用时,他们分别消耗了我的请求时间的大约1/3和1/2.绝对不容忽视.更多地了解它http://stackoverflow.com/a/31569634/364604.

4> SilentGhost..:
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"和"xb"这样的"twigs",这些"twigs"返回了丑陋的``

5> umbrae..:

有一个名为的集合帮助器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


这不能回答嵌套dicts的递归问题.
你不能修改namedtuples.

6> 小智..:
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

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


Python 3.x用户:它只是`.items()`而不是第4行的`.iteritems()`.(该函数已重命名,但确实[必须相同](http://stackoverflow.com/a/二百五十二万五千二百九十九分之一千〇四十五万八千五百六十七))
这应该是答案.它适用于嵌套.你也可以将它用作json.load()的object_hook.

7> andyvanee..:

我认为这是前面例子中最好的方面,这就是我想出的:

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


我宁愿不回击我自己的答案,但回头看我已经注意到它没有递归到序列类型.xd [1] .foo在这种情况下失败了.
`isinstance(v,dict)`检查会更好``isinstance(v,collections.Mapping)`所以它可以处理未来类似dict的东西

8> DS...:

如果你的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对象.



9> 小智..:

如果你想要将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'}}



10> Anon..:
>>> 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'



11> JayD3e..:

我最终尝试了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对象上根据需要编写它们.



12> 小智..:

x.__dict__.update(d) 应该没事.


这不会处理嵌套的词典.

13> Aaron Digull..:

这应该让你开始:

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.


要使它适用于列表,请使用`Anon`的答案中的`if isinstance(d,list)`子句.

14> 小智..:

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

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有