字典`__getitem__`多订阅覆盖

问题描述 投票:0回答:2

我正在尝试实现dict数据结构的自定义行为。

我想覆盖__getitem__并在将值返回给用户之前对值应用某种正则表达式。

片段:

class RegexMatchingDict(dict):
    def __init__(self, dct, regex, value_group, replace_with_group, **kwargs):
        super().__init__(**kwargs)
        self.replace_with_group = replace_with_group
        self.value_group = value_group
        self.regex_str = regex
        self.regex_matcher = re.compile(regex)
        self.update(dct)

    def __getitem__(self, key):
        value: Union[str, dict] = dict.__getitem__(self, key)
        if type(value) is str:
            match = self.regex_matcher.match(value)
            if match:
                return value.replace(match.group(self.replace_with_group), os.getenv(match.group(self.value_group)))
        return value # I BELIEVE ISSUE IS HERE

这适用于单个索引级别(即dict[key])。但是,当尝试对其进行多索引时(即dict[key1][key2]),会发生的情况是第一个索引级别从我的类返回一个对象。但是,其他级别调用__getitem__中的默认dict,它不执行我的自定义行为。我怎样才能解决这个问题?


MCVE:

前面提到的代码将正则表达式应用于值,并将其转换为相应的环境变量的值,如果它是字符串(即,dict中的最低级别)

dictionary = {"KEY": "{ENVIRONMENT_VARIABLE}"}

custom_dict = RegexMatchingDict(dictionary, r"((.*({(.+)}).*))", 4 ,3)

让我们设置一个名为ENVIRONMENT_VARIABLE的env变量设置为1

import os

os.environ["ENVIRONMENT_VARIABLE"] = "1"

在这种情况下,你的代码完全正常

custom_dict["KEY"]

并且返回的值将是:

{"KEY": 1}

但是,如果我们有一个多级索引

dictionary = {"KEY": {"INDEXT_KEY": "{ENVIRONMENT_VARIABLE}"}
custom_dict = RegexMatchingDict(dictionary, r"((.*({(.+)}).*))", 4 ,3)
custom_dict["KEY"]["INDEX_KEY"]

这会回来

{ENVIRONMENT_VARIABLE}

P. S.有许多类似的问题,但它们都(可能)解决了顶级索引问题。

python dictionary override
2个回答
1
投票

正如您所说,问题出现在代码的最后一行。

if type(value) is str:
    ...
else:
    return value # I BELIEVE ISSUE IS HERE

这是返回一个dict。但是你想要返回一个RegexMatchingDict,它将知道如何处理第二级索引。因此,如果它是value而不是返回dict,将其转换为RegexMatchingDict并返回它。然后,当调用__getitem__()执行第二级索引时,您将获得您的版本而不是标准版本。

像这样的东西:

return RegexMatchingDict(value, self.regex_str, self.value_group, self.replace_with_group)

这复制了第一级别的其他参数,因为很难看出第二级别如何不同。


0
投票

在您的示例中,您的二级字典是正常的dict,因此不使用您的自定义__getitem__方法。

下面的代码显示了如何使用内部自定义dict

sec_level_dict = {"KEY": "{ENVIRONMENT_VARIABLE}"}

sec_level_custom_dict = RegexMatchingDict(sec_level_dict, r"((.*({(.+)}).*))", 4 ,3)

dictionary = {"KEY": sec_level_custom_dict}
custom_dict = RegexMatchingDict(dictionary, r"((.*({(.+)}).*))", 4 ,3)
print(custom_dict["KEY"]["KEY"])

如果你想自动化它并在自定义dict中转换所有嵌套的dict,你可以按照这种模式自定义__setitem__

class CustomDict(dict):

    def __init__(self, dct):
        super().__init__()
        for k, v in dct.items():
            self[k] = v

    def __getitem__(self, key):
        value = dict.__getitem__(self, key)
        print("Dictionary:", self, "key:", key, "value:", value)
        return value

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            dict.__setitem__(self, key, self.__class__(value))
        else:
            dict.__setitem__(self, key, value)

a = CustomDict({'k': {'k': "This is my nested value"}})

print(a['k']['k'])
© www.soinside.com 2019 - 2024. All rights reserved.