使用Python检测对嵌套字典的更改

时间:2018-12-21 09:30:19

标签: python dictionary

我已经在SO上看到了一些类似的问题,例如关于检测字典的更改并在字典更改时调用函数的问题,例如:

这些示例使用了Observer模式的变体或__setitem__重载,但是所有这些示例均未检测到嵌套字典值的更改。

例如,如果我有:

my_dict = {'a': {'b': 1}}
my_dict['a']['b'] = 2

将不会检测到2对元素['a']['b']的分配。

我想知道是否有一种优雅的方法不仅可以检测字典的基本元素,还可以检测嵌套字典的所有子元素的变化。

3 个答案:

答案 0 :(得分:5)

here中给出的答案为基础,只需执行以下操作:

class MyDict(dict):
    def __setitem__(self, item, value):
        print("You are changing the value of {} to {}!!".format(item, value))
        super(MyDict, self).__setitem__(item, value)

然后:

my_dict = MyDict({'a': MyDict({'b': 1})})

my_dict['a']['b'] = 2
  

您正在将b的值更改为2 !!

my_dict['a'] = 5
  

您正在将a的值更改为5 !!

如果要避免在每个嵌套级别手动调用MyDict,一种方法是完全重载 dict 类。例如:

class MyDict(dict):
    def __init__(self,initialDict):
        for k,v in initialDict.items():
          if isinstance(v,dict):
            initialDict[k] = MyDict(v)
        super().__init__(initialDict)

    def __setitem__(self, item, value):
        if isinstance(value,dict):
          _value = MyDict(value)
        else:
          _value = value
        print("You are changing the value of {} to {}!!".format(item, _value))
        super().__setitem__(item, _value)

然后您可以执行以下操作:

# Simple initialization using a normal dict synthax
my_dict = MyDict({'a': {'b': 1}})

# update example
my_dict['c'] = {'d':{'e':4}}
  

您正在将c的值更改为{'d':{'e':4}} !!

my_dict['a']['b'] = 2
my_dict['c']['d']['e'] = 6
  

您正在将b的值更改为2 !!

     

您正在将e的值更改为6!

答案 1 :(得分:1)

this链接(OP给出的第二个链接)中借用完整的解决方案

class MyDict(dict):
    def __setitem__(self, item, value):
        print("You are changing the value of {key} to {value}!!".format(key=item, value=value))
        super(MyDict, self).__setitem__(item, convert_to_MyDict_nested(value))

def convert_to_MyDict_nested(d):
    if not(isinstance(d, dict)):
        return d
    for k, v in d.items():
        if isinstance(v, dict):
            d[k] = convert_to_MyDict_nested(v)
    return MyDict(d)

如果

d = {'a': {'b': 1}}

然后

d = convert_to_MyDict_nested(d)
d['a']['b'] = 2  # prints You are changing the value of b to 2!!
d['a']= 5  # prints You are changing the value of a to 5!!

此外,根据OP的评论进行编辑。所以,

d["c"] = {"e" : 7}  # prints You are changing the value of c to {'e': 7}!!
d["c"]["e"] = 9  # prints You are changing the value of e to 9!!

答案 2 :(得分:1)

您可以编写一个适配器类,将其自身自动包装值,如下所示(未经测试,但我认为它说明了这一点):

class DictChangeListenerAdapter:
    def __init__(self, original, listener):
        self._original = original
        self._listener = listener

    def __getitem__(self, key):
        value = self._original.__getitem__(key)
        if isinstance(value, dict):
            return DictChangeListenerAdapter(self._original[key], self._listener)
        else:
            return value

    def __setitem__(self, key, value):
        self._original.__setitem__(key, value)
        self._listener(self, key, value)

请注意,这将导致对包装物品的访问变得更加昂贵,请谨慎使用。