代码之家  ›  专栏  ›  技术社区  ›  Ryan Schaefer

使用dict的dict创建命名空间

  •  2
  • Ryan Schaefer  · 技术社区  · 8 年前

    目前我正在使用 json 将dict保存到配置文件。我装这个把它变成 dict 然后把它变成 SimpleNamespace 因为我更喜欢点标记来访问设置。要做到这一点,我将像在本例中那样加载它:

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

    但是,由于我目前正在加载 双关语 变成一个 单纯形命名空间 它没有在配置文件中加载子dict。例如,如果我这样做:

    SETTINGS.server_info.port
    

    我得到错误:

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

    我想知道如何将所有dict作为名称空间加载到名称空间中,以便能够在字典中一直使用点标记。

    2 回复  |  直到 8 年前
        1
  •  7
  •   larsks    8 年前

    不一定要递归地将转换应用于 json.load . 你可以简单地问 json.load.加载 返回 SimpleNamespace 通过提供 object_hook 方法 json.load.加载 呼叫。将调用此方法“,并对每个JSON对象的结果进行解码,并使用其返回值代替给定的 dict “(来自 docs )

    最简单的 物体挂钩 可能看起来像:

    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']
    
        2
  •  3
  •   Martijn Pieters    8 年前

    您必须递归地应用 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'