Обнаружение изменений во вложенном словаре с помощью Python - PullRequest
0 голосов
/ 21 декабря 2018

Я видел несколько похожих вопросов по SO, касающихся обнаружения изменений в словаре и вызова функции при изменении словаря, например:

В этих примерах используются варианты шаблона Observer или перегрузки __setitem__, но всеэти примеры не обнаруживают изменений во вложенных словарных значениях.

Например, если у меня есть:

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

Назначение 2 элементу ['a']['b'] не будет обнаружено.

Мне интересно, существует ли элегантный способ обнаружения изменений не только в базовых элементах словаря, но и во всех дочерних элементах вложенного словаря.

Ответы [ 3 ]

0 голосов
/ 21 декабря 2018

Полное решение заимствования из этой ссылки (второй даной 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!!

Также отредактировано в соответствии с комментарием ОП.Итак,

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!!
0 голосов
/ 21 декабря 2018

Вы можете написать класс адаптера, который автоматически упаковывает значения в себя, например так (не проверено, но я думаю, что это иллюстрирует смысл):

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)

Обратите внимание, что это вызовет доступ к перенесенномупредметы должны быть намного дороже, используйте с осторожностью.

0 голосов
/ 21 декабря 2018

Опираясь на ответ, данный в здесь , просто сделайте следующее:

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 !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...