设置值时调用函数
Calling a function when setting a value
使用观察者模式,我有一个 dict
的子 class,其中我重写 __setitem__
以在 dict 中的值发生更改时调用函数。这对于 my_dict[key] = 1234
等直接赋值非常有用,但当我想更改存储在字典中的数组中的值时不起作用,例如 my_dict[key][0] = 1
.
由于这是一个异步应用程序,有人认为我有一个计划是在 __getitem__
中安排一个未来,应用程序在那里等待片刻,然后将密钥处的旧内容与当前内容进行比较,但这显然可以导致竞争条件。
处理此问题的最佳方法是什么?每当我想在字典中存储数组时,我都不想使用自定义数组。
编辑:这是 class
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]()
AFAIR,几年前在 Python 中子类化内置类型曾经是个坏主意。除此之外,我认为没有一个简洁的解决方案可以让 dict 意识到其任何值的变化。您需要此功能做什么?
也许你可以写一个 list
的子 class 并覆盖它的 __getitem__
函数,你将在你的 dict
子时使用这个 class class __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__
功能。我在 __setitem__
函数中添加了一个 print(value)
行,以便在代码经过 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'
使用观察者模式,我有一个 dict
的子 class,其中我重写 __setitem__
以在 dict 中的值发生更改时调用函数。这对于 my_dict[key] = 1234
等直接赋值非常有用,但当我想更改存储在字典中的数组中的值时不起作用,例如 my_dict[key][0] = 1
.
由于这是一个异步应用程序,有人认为我有一个计划是在 __getitem__
中安排一个未来,应用程序在那里等待片刻,然后将密钥处的旧内容与当前内容进行比较,但这显然可以导致竞争条件。
处理此问题的最佳方法是什么?每当我想在字典中存储数组时,我都不想使用自定义数组。
编辑:这是 class
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]()
AFAIR,几年前在 Python 中子类化内置类型曾经是个坏主意。除此之外,我认为没有一个简洁的解决方案可以让 dict 意识到其任何值的变化。您需要此功能做什么?
也许你可以写一个 list
的子 class 并覆盖它的 __getitem__
函数,你将在你的 dict
子时使用这个 class class __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__
功能。我在 __setitem__
函数中添加了一个 print(value)
行,以便在代码经过 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'