繁体   English   中英

使用可选键将配置字典转换为 Python 对象

[英]Converting a configuration dictionary to a Python object with optional keys

假设我有一堂课:

class Foo():
    """A class with attr1 and attr2."""
    def __init__(self, attr_1: str = "", attr_2: int = 0):
        self.attr_1 = attr_1
        self.attr_2 = attr_2

和一个配置字典:

{
    "attr_1": "qack",
    "attr_2": 10
}

请注意,有时,配置 dict 可能具有不同的键名,但在内部,程序有一种方法可以将 dict 中的键值映射到Foo属性中的相应属性。

{
    "attr_1_diff_key_name": "qack",
    "2_attr": 10
}

我希望能够编写两个将配置字典映射到Foo的函数,反之亦然(也就是能够将配置字典转换为Foo对象,反之亦然),如下所示:

def dict_to_foo(dict_config: dict):
    """Returns the Foo equivalent of the dictionary."""
    return Foo(
        attr_1 = dict_config["attr_1"],
        attr_2 = dict_config["attr_2"]
    )


def foo_to_dict(foo: Foo):
    """Returns the dictionary equivalent of the Foo object."""
    return {
        "attr_1": foo.attr_1,
        "attr_2": foo.attr_2
    }

它非常简单,但仅当配置字典需要包含所有属性时。

在我的项目中,情况并非如此。 config dict 可能会省略一些属性,其中假定程序将使用默认值来替换缺少的属性。

config_dict_examples = [
    # Usual, has all attributes
    {
        "attr_1": "qack",
        "attr_2": 10
    },

    # "attr_2" omitted, it's understood here that the Foo equivalent will have attr_2 set to its default value from the __init__ function, in this case, 0.
    {
        "attr_1": "qack"
    },

    # In this case, all attributes are omitted, and thus the Foo equivalent will have both attr_1 and attr_2 set to its default value from the __init__ function, in this case, "" and 0 respectively.
    {}
]

我对这个问题的尝试如下:

def dict_to_foo_attempt_1(dict_config: dict):
    """Attempt 1 of dict_to_foo."""
    foo = Foo()

    if "attr_1" in dict_config:
        foo.attr_1 = dict_config["attr_1"]
    if "attr_2" in dict_config:
        foo.attr_2 = dict_config["attr_2"]

def foo_to_dict_attempt_1(foo: Foo):
    """Attempt 1 of foo_to_dict."""
    dict_config = {}
    default_foo = Foo()

    if foo.attr_1 != default_foo.attr_1:
        dict_config["attr_1"] = foo.attr_1
    if foo.attr_2 != default_foo.attr_2:
        dict_config["attr_2"] = foo.attr_2

当然,这可能很好,但是从条件表达式和两个函数中的键/变量的设置来看,一般的重复并不是我真正想要的。

有没有办法让这些功能更干净? (比如说,没有重复的部分?)


需要使用**kwargsvars()之类的解决方案的一个常见问题是它有点太 hacky 并且不受vscode 的重命名功能的支持,这使得将来难以重构。 这不是我想要的,但如果没有其他办法,我仍然可以做到。

最干净的处理方式:

def dict_to_foo(dict_config: dict):
    """Returns the Foo equivalent of the dictionary."""
    return Foo(**dict_config)


def foo_to_dict(foo: Foo):
    """Returns the dictionary equivalent of the Foo object."""
    return vars(foo).copy()

假设您有一个带有真实属性名称的配置字典(您提到有一个映射),您可以迭代字典并使用setattr仅设置必要的属性。

class Foo():
    """A class with attr1 and attr2."""
    def __init__(self, attr_1: str = "default_value", attr_2: int = 0):
        self.attr_1 = attr_1
        self.attr_2 = attr_2

config = {
    "attr_1": "qack",
    # "attr_2": 10
}

foo_obj = Foo()
print('Before using config: ',foo_obj.attr_1, foo_obj.attr_2)
for key, value in config.items():
    setattr(foo_obj, key, value)
print('After config: ',foo_obj.attr_1, foo_obj.attr_2)

输出:

Before using config:  default_value 0
After config:  qack 0

您可以使用字典将参数名称映射到字典键,反之亦然:

FOO_ATTR_TO_KEY = {
    'attr_1': 'attr_1_diff_key_name',
}
FOO_KEY_TO_PARAM = {v: k for k, v in FOO_ATTR_TO_KEY.items()}

def dict_to_foo(dict_config: dict):
    kwargs = {
        FOO_KEY_TO_PARAM.get(key, key): value
        for key, value in dict_config.items()
    }
    return Foo(**kwargs)

def foo_to_dict(foo: Foo):
    return {
        FOO_ATTR_TO_KEY.get(name, name): value
        for name, value in vars(foo).items()
    }

有一种更清洁的方法可以做到这一点。

您可以在 python 中更新对象的内部状态,因为默认情况下它使用字典。

举个例子

a1 = {"attr_1": "qack", "attr_2": 10}
a2 = {"attr_1": "woof"}
a3 = {"name": "John", "health": -10}

和一堂课

class Foo:
    def __init__(self, **attrs):
        self.__dict__.update(attrs)

现在我们可以打包字典并将它们传递给构造函数,如下所示:

f1 = Foo(**a1)
print(f1.__dict__) # {'attr_1': 'qack', 'attr_2': 10}
f2 = Foo(**a2)
print(f2.__dict__) # {'attr_1': 'woof'}
f3 = Foo(**a3)
print(f3.__dict__) # {'name': 'John', 'health': -10}

print(f2.attr_1) # woof
print(f3.name) # John

这为您提供了相同的功能,而您需要编写的开销代码更少。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM