Dictionay`__getitem__`多订阅覆盖

时间:2019-04-05 10:19:40

标签: python dictionary override

我正在尝试实现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的环境变量设置为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.有许多相似问题,但它们都(可能)涉及顶级索引。

2 个答案:

答案 0 :(得分: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)

由于很难看到第二层的差异,因此这会复制第一层的其他参数。

答案 1 :(得分: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'])