替代嵌套AttrDict的库有哪些?

3

我的程序包含许多配置参数,因此我正在寻找一种方法,将它们全部放在一个地方,并可从项目中的每个文件中访问。 我考虑使用一个配置模块,它将充当访问包含配置参数的yaml文件的接口。 我希望满足的一个要求是能够使用属性(点)表示法访问配置对象。 我找到了 AttrDict 库,并编写了以下代码:

import yaml
from attrdict import AttrDict


def get_cfg():
    return cfg_node


def dump(out_filepath):
    with open(out_filepath, 'w') as file:
        yaml.dump(attrdict_to_dict(cfg_node), file)


def load_yaml(filepath):
    global cfg_node
    with open(filepath, 'r') as file:
        cfg_node = dict_to_attrdict(yaml.safe_load(file))


def attrdict_to_dict(myAttrdict):
    dictionary = dict(myAttrdict)
    for k in myAttrdict:
        value = dictionary[k]
        if isinstance(value, AttrDict):
            dictionary[k] = attrdict_to_dict(value)
    return dictionary


def dict_to_attrdict(dictionary):
    myAttrdict = AttrDict()
    for k in dictionary:
        value = dictionary[k]
        if isinstance(value, dict):
            myAttrdict.__setattr__(k, attrdict_to_dict(value))
    return myAttrdict


cfg_node = AttrDict()
cfg_node.b = "value_b"
cfg_node.a = AttrDict()
cfg_node.a.b = AttrDict()
cfg_node.a.c = "nested_c"
cfg_node.a.b.e= "double_nested_e"

问题在于此库不允许嵌套使用AttrDict()。

print(cfg_node)
 >>> {'b': 'value_b', 'a': AttrDict({})}

然而,如果我执行以下操作,嵌套赋值就可以实现,但是我被迫显式使用嵌套对象,这对我的目的没有用处:
cfg_node = AttrDict()
cfg_node.b = "value_b"
a =  AttrDict()
a.c = "nested_value_c"
cfg_node.a = a
print(cfg_node)
 >>> {'b': 'value_b', 'a': AttrDict({'c':'nested_value_c'})}
3个回答

2

我已经查阅了AttrDict的文档,恐怕递归属性访问是不可能的。请参见以下内容:

Recursive attribute access results in a shallow copy, so recursive assignment will fail (as you will be writing to a copy of that dictionary):

> attr = AttrDict('foo': {})
> attr.foo.bar = 'baz'
> attr.foo
{}
他们建议使用 AttrMap。然而我尝试使用它时,从导入开始就出现了错误。我会研究一下的。*下面更新
目前来说,你可以定义自己的自定义 AttrDict
class AttrDict(dict):

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


if __name__ == '__main__':
    cfg_node = AttrDict()
    cfg_node.b = "value_b"
    cfg_node.a = AttrDict()
    cfg_node.a.b = AttrDict()
    cfg_node.a.c = "nested_c"
    cfg_node.a.b.e = "double_nested_e"
    print(cfg_node)
    print(cfg_node.a.b.e)

这里的输出结果正如您需要的那样:
{'b': 'value_b', 'a': {'b': {'e': 'double_nested_e'}, 'c': 'nested_c'}}
double_nested_e

如果您在使用IDE时访问 AttrDict 的键,将会得到代码完成的功能。
此外,请参阅这里,有一些额外的方法可以添加到自定义类中,以增强其功能。
希望这能对您有所帮助!
关于 AttrMap 库的更新。我在这里询问了它,我使用的是Python 3.8,但该库包含了一个仅在Python> = 3.9上可用的list [str]
关于 AttrMap 的第二次更新。创建者已经在新版本中修复了这个问题,现在它支持Python 3.6。

1

在代码中表示配置

我喜欢使用pydanticdataclasses来处理这种情况。我觉得pydantic使得代码变得非常干净和简单。

from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: Optional[datetime] = None
    friends: List[int] = []

您可以像创建普通对象一样创建对象,从 JSON / YAML(或任何创建字典的东西)中解析它。您可以拥有验证逻辑和数据转换。

类似属性的访问

如果只是为了快速创建对象:types.SimpleNamespace

from types import SimpleNamespace

sn = SimpleNamespace(param_a='1')
print(sn.param_a)

0

看看模块attridict,它提供了你所需要的功能。 它可以100%支持嵌套字典。 它基本上的工作方式是相同的。

> att = attridict({'foo': {}})
> att.foo.bar = 'baz'
> att.foo
{'bar':'baz'}

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