Вызов функции при установке значения - PullRequest
0 голосов
/ 14 марта 2019

Используя шаблон наблюдателя, у меня есть подкласс dict, где я переопределяю __setitem__ для вызова функции всякий раз, когда значение в dict изменяется. Это прекрасно работает для прямых назначений, таких как my_dict[key] = 1234, но не работает, когда я хочу изменить значение в массиве, хранящемся в dict, таком как my_dict[key][0] = 1.

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

Каков наилучший способ справиться с этим? Я бы предпочел не использовать пользовательский массив в любое время, когда я хочу сохранить массив в моем файле.

РЕДАКТИРОВАТЬ: вот класс

class SettingsTable(dict):
    def __init__(self, seq=None, **kwargs):
        super().__init__({}, **kwargs)
        self._post_process_map = {}

    def add_post_process(self, key, process, *args):
        if not self._post_process_map.__contains__(key):
            self._post_process_map[key] = []
        self._post_process_map[key].append((process, *args))

    def __setitem__(self, key, value):
        old_val = None
        if self.keys().__contains__(key):
            old_val = self[key]

        super().__setitem__(key, value)
        self._run_on_change_funcs(key, value, old_val)

    def _run_on_change_funcs(self, key, value, old_val):
        if not old_val.__eq__(value):
            if self._post_process_map.__contains__(key):
                for func in self._post_process_map[key]:
                    if func[1]:
                        func[0](func[1])
                    else:
                        func[0]()

Ответы [ 2 ]

1 голос
/ 14 марта 2019

Возможно, вы можете написать подкласс list, а также переопределить его функцию __getitem__, вы будете использовать этот класс всякий раз, когда ваша функция dict подкласс __getitem__ получит значение типа list.Это какое-то хакерское решение, но оно должно работать в вашем случае

def change_item(val):
    print 'item changed: ' + str(val)

class MyList(list):
    def __setitem__(self, key, val):
        change_item(val)
        list.__setitem__(self, key, val)

class MyDict(dict):
    def __setitem__(self, key, val):
        change_item(val)
        if isinstance(val, list):
            dict.__setitem__(self, key, MyList(val))
        else:
            dict.__setitem__(self, key, val)

md = MyDict()
md['a'] = 1
md['d'] = ['c', 'd', 'e', 'f']
md['d'][0] = 'b'

Тест вывода, который я получил:

item changed: 1
item changed: ['c', 'd', 'e', 'f']
item changed: b

Обновление: я только что проверил ваш код.Я применил тот же подход, что и предлагал, но я также сохраняю ссылку на словарь и его ключ в объекте MyList, чтобы при изменении объекта MyList он создавал дублирующийся объект MyList, назначая новое значениеэто и назначить значение объекта словаря при ключевой ссылке равным дублирующему объекту MyList, таким образом вызывая функцию SettingsTable __setitem__.Я добавил строку print(value) в функцию __setitem__, чтобы проверить, вызывается ли она или нет, когда код проходит через md['d'][0]='b' строку

class MyList(list):
    def __init__(self, dictionary, key, *args):
        super().__init__(*args)
        self.dictionary = dictionary
        self.key = key
    def __setitem__(self, key, val):
        new_list = MyList(self.dictionary, self.key, list(self))
        list.__setitem__(new_list, key, val)
        self.dictionary[self.key] = new_list

class SettingsTable(dict):
    def __init__(self, seq=None, **kwargs):
        super().__init__({}, **kwargs)
        self._post_process_map = {}

    def add_post_process(self, key, process, *args):
        if not self._post_process_map.__contains__(key):
            self._post_process_map[key] = []
        self._post_process_map[key].append((process, *args))

    def __setitem__(self, key, value):
        old_val = None
        if self.keys().__contains__(key):
            old_val = self[key]

        if isinstance(value, list):
            value = MyList(self, key, value)

        super().__setitem__(key, value)
        self._run_on_change_funcs(key, value, old_val)

    def _run_on_change_funcs(self, key, value, old_val):
        print(value)
        if not old_val.__eq__(value):
            if self._post_process_map.__contains__(key):
                for func in self._post_process_map[key]:
                    if func[1]:
                        func[0](func[1])
                    else:
                        func[0]()

md = SettingsTable()
md['a'] = 1
md['d'] = ['c', 'd', 'e', 'f']
md['d'][0] = 'b'

0 голосов
/ 14 марта 2019

AFAIR, раньше было плохой идеей создавать подклассы для встроенных типов в Python несколько лет назад. Помимо этого, я не думаю, что есть краткое решение, с помощью которого диктат может быть осведомлен об изменениях любой из его ценностей. Для чего вам нужен этот функционал?

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