嵌套字典的类似对象的属性访问

时间:2016-06-26 01:09:31

标签: python dictionary nested bunch

我正在使用一个返回嵌套字典的包。 当其他所有内容都在对象语法中时,使用字典语法在类方法中访问此返回对象会感到很尴尬。 搜索带给我一堆/新推出的套餐,这似乎达到了我之后的目标。我也看到过namedtuples的建议,但这些不容易支持嵌套属性,大多数解决方案依赖于在namedtuple中使用字典进行嵌套。

实现这一目标的更自然的方式是什么?

data = {'a': 'aval', 'b': {'b1':{'b2a':{'b3a':'b3aval','b3b':'b3bval'},'b2b':'b2bval'}} }

print(data['b']['b1']['b2a']['b3b'])  # dictionary access
# print(data.b.b1.b2a.b3b)  # desired access

import neobunch
data1 = neobunch.bunchify(data)
print(data1.b.b1.b2a.b3b)

6 个答案:

答案 0 :(得分:4)

以下课程可以让你做你想做的事:

class AttrDict(dict):
    """ Dictionary subclass whose entries can be accessed by attributes
        (as well as normally).
    """
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

    @staticmethod
    def from_nested_dict(data):
        """ Construct nested AttrDicts from nested dictionaries. """
        if not isinstance(data, dict):
            return data
        else:
            return AttrDict({key: AttrDict.from_nested_dict(data[key])
                                for key in data})

data = {
    "a": "aval",
    "b": {
        "b1": {
            "b2b": "b2bval",
            "b2a": {
                "b3a": "b3aval",
                "b3b": "b3bval"
            }
        }
    }
}

data1 = AttrDict.from_nested_dict(data)
print(data1.b.b1.b2a.b3b)  # -> b3bval

答案 1 :(得分:0)

可以使用基于基本对象构建的简单类:

class afoo1(object):
    def __init__(self, kwargs):
        for name in kwargs:
            val = kwargs[name]
            if isinstance(val, dict):
                val = afoo1(val)
            setattr(self,name,val)

我借用argparse.Namespace定义,调整以允许嵌套。

它将用作

In [172]: dd={'a':'aval','b':{'b1':'bval'}}

In [173]: f=afoo1(dd)

In [174]: f
Out[174]: <__main__.afoo1 at 0xb3808ccc>

In [175]: f.a
Out[175]: 'aval'

In [176]: f.b
Out[176]: <__main__.afoo1 at 0xb380802c>

In [177]: f.b.b1
Out[177]: 'bval'

也可以使用**kwargs(以及*args)进行定义。 __repr__定义也可能很好。

与其他简单对象一样,可以添加属性,例如f.c = f(递归定义)。 vars(f)返回字典,但它不进行任何递归转换。)

答案 2 :(得分:0)

使用__setattr__方法怎么样?

>>> class AttrDict(dict):
...     def __getattr__(self, name):
...         if name in self:
...             return self[name]
... 
...     def __setattr__(self, name, value):
...         self[name] = self.from_nested_dict(value)
... 
...     def __delattr__(self, name):
...         if name in self:
...             del self[name]
... 
...     @staticmethod
...     def from_nested_dict(data):
...         """ Construct nested AttrDicts from nested dictionaries. """
...         if not isinstance(data, dict):
...             return data
...         else:
...             return AttrDict({key: AttrDict.from_nested_dict(data[key])
...                                 for key in data})
...         

>>> ad = AttrDict()
>>> ad
{}

>>> data = {'a': 'aval', 'b': {'b1':{'b2a':{'b3a':'b3aval','b3b':'b3bval'},'b2b':'b2bval'}} }

>>> ad.data = data
>>> ad.data
{'a': 'aval', 'b': {'b1': {'b2a': {'b3a': 'b3aval', 'b3b': 'b3bval'}, 'b2b': 'b2bval'}}}

>>> print(ad.data.b.b1.b2a.b3b)
    b3bval

答案 3 :(得分:0)

基于@martineau's的出色答案,您可以使AttrDict类在嵌套字典上工作,而无需显式调用from_nested_dict()函数:

class AttrDict(dict):
""" Dictionary subclass whose entries can be accessed by attributes
    (as well as normally).
"""
def __init__(self, *args, **kwargs):
    def from_nested_dict(data):
        """ Construct nested AttrDicts from nested dictionaries. """
        if not isinstance(data, dict):
            return data
        else:
            return AttrDict({key: from_nested_dict(data[key])
                                for key in data})

    super(AttrDict, self).__init__(*args, **kwargs)
    self.__dict__ = self

    for key in self.keys():
        self[key] = from_nested_dict(self[key])

答案 4 :(得分:0)

json.loads有一个有趣的参数,称为object_hook,如果所有字典值都是JSON可序列化的,即

,则可以使用该参数。
import json
from types import SimpleNamespace

data = {'a': 'aval', 'b': {'b1':{'b2a':{'b3a':'b3aval','b3b':'b3bval'},'b2b':'b2bval'}}}
data1= json.loads(
    json.dumps(data), object_hook=lambda d: SimpleNamespace(**d)
)
print(data1.b.b1.b2a.b3b)  # -> b3bval

如果Guido在听,我认为SimpleNamespace应该使用recursive参数,以便您可以执行data1 = SimpleNamespace(recursive=True, **data)

答案 5 :(得分:0)

尝试DotsiEasyDict。它们都支持嵌套字典的点符号。

>>> import dotsi
>>> data = dotsi.fy({'a': 'aval', 'b': {'b1':{'b2a':{'b3a':'b3aval','b3b':'b3bval'},'b2b':'b2bval'}} })
>>> print(data.b.b1.b2a.b3b)
b3bval
>>> 

除了字典中的字典外,Dotsi还支持列表中的字典中的字典。
注意:我是Dotsi的作者。