设置值时调用函数

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

使用观察者模式,我有一个dict的子类,其中我重写__setitem__,以便在更改dict中的值时调用函数。这对于my_dict[key] = 1234这样的直接赋值很有用,但是当我想要更改存储在dict中的数组中的值(例如my_dict[key][0] = 1)时,它不起作用。

由于这是一个异步应用程序,我想到的是在__getitem__安排未来应用程序等待片刻,然后将密钥的旧内容与当前内容进行比较,但这显然会导致竞争条件。

处理这个问题的最佳方法是什么?我希望在我想要在我的dict中存储数组时不必使用自定义数组。

编辑:这是班级

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]()
python python-3.x dictionary observer-pattern
2个回答
1
投票

也许你可以写一个list的子类并覆盖它的__getitem__函数,只要你的dict子类__getitem__函数接收到list类型值,你就会使用这个类。这有点hacky解决方案,但它应该适用于您的情况

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
投票

AFAIR,几年前在Python中子类化内置类型曾经是一个坏主意。除此之外,我认为没有一个简洁的解决方案可以让dict知道其任何值的变化。你需要什么功能?

© www.soinside.com 2019 - 2024. All rights reserved.