使用字典创建一个命名空间

9

目前我正在使用json将字典保存到配置文件中。我加载它以将其转换为dict,然后将其转换为SimpleNamespace,因为我更喜欢使用点表示法来访问设置。为了做到这一点,我按照以下示例进行加载:

import json
from types import SimpleNamespace
SETTINGS = json.load(open("config.json", 'r'))
SETTINGS = SimpleNamespace(**SETTINGS)

然而,由于我目前正在将 dict 加载到一个 SimpleNamespace 中,因此它没有加载配置文件中的子字典。比如,如果我执行以下操作:

SETTINGS.server_info.port

我遇到了错误:

AttributeError: 'dict' object has no attribute 'port'

我想知道如何将所有字典加载到命名空间中,以便我能够在整个字典中使用点符号表示法。
2个回答

21

不需要递归地对json.load返回的数据应用转换。你可以通过向json.load调用提供一个object_hook方法,简单地要求json.load返回SimpleNamespace实例而不是字典。这个方法“将被调用来处理每个JSON对象,并且它的返回值将被用作给定dict的替代品。” (来源于文档

最简单的object_hook可能如下所示:

def dict_to_sns(d):
    return SimpleNamespace(**d)
例如,给定以下输入:
{
  "settings": {
    "foo": {
      "number": 4,
      "size": "large"
    },
    "bar": {
      "color": "orange",
      "widgets": [
        "gizmo",
        "gadget",
        "thing"
      ]
    }
  }
}

我们可以做以下事情:

>>> import json
>>> from types import SimpleNamespace
>>> def dict_to_sns(d):
...     return SimpleNamespace(**d)
... 
>>> with open('settings.json') as fd:
...     data = json.load(fd, object_hook=dict_to_sns)
... 
>>> data
namespace(settings=namespace(bar=namespace(color='orange', widgets=['gizmo', 'gadget', 'thing']), foo=namespace(number=4, size='large')))
>>> data.settings.foo
namespace(number=4, size='large')
>>> data.settings.foo.number
4
>>> data.settings.bar.widgets
['gizmo', 'gadget', 'thing']

13

你需要对嵌套字典递归应用SimpleNamespace类;我更喜欢在这种情况下使用@functools.singledispatch()

from functools import singledispatch
from types import SimpleNamespace

@singledispatch
def wrap_namespace(ob):
    return ob

@wrap_namespace.register(dict)
def _wrap_dict(ob):
    return SimpleNamespace(**{k: wrap_namespace(v) for k, v in ob.items()})

@wrap_namespace.register(list)
def _wrap_list(ob):
    return [wrap_namespace(v) for v in ob]

然后将其用作:

with open('config.json') as settings_file:
    SETTINGS = wrap_namespace(json.load(settings_file))

演示:

>>> SETTINGS = wrap_namespace({'foo': 'bar', 'ham': {'spam': 'eggs', 'monty': [{'name': 'Eric Idle'}]}})
>>> SETTINGS.foo
'bar'
>>> SETTINGS.ham.monty[0].name
'Eric Idle'

另请参阅:functools.singledispatchmethod - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接