【问题标题】:Calling a function when setting a value设置值时调用函数
【发布时间】:2019-03-14 17:42:23
【问题描述】:

使用观察者模式,我有一个 dict 的子类,我在其中覆盖 __setitem__ 以在 dict 中的值发生更改时调用函数。这对于直接赋值(例如my_dict[key] = 1234)非​​常有效,但当我想更改存储在字典中的数组中的值(例如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]()

【问题讨论】:

  • this question 没有帮助怎么办?听起来他们有同样的问题。
  • 你可以让你的__setitem__接受元组,这样你就可以用my_dict[key, 0] = 1代替my_dict[key][0] = 1。然后所有的特殊外壳都在您的dict 子类中。当然,这并不妨碍人们采用第一种方式。但是,比较是正确的。您需要保留所有更改的列表的副本。
  • my_dict[key][0] = 1, my_dict 对任何作业一无所知;该对象所看到的只是key 的查找。完成这项工作的唯一方法是让可变对象的每次查找都返回实际对象的包装版本,该版本具有自己的 __setitem__() 覆盖。
  • 当分配给一个可下标值的项目时,你想要调用相同的函数吗?

标签: python python-3.x dictionary observer-pattern


【解决方案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__ 函数。我在__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'

【讨论】:

  • 这很可能是我要做的。这也允许嵌套列表/字典,因为它们可以链接在一起。
【解决方案2】:

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

【讨论】:

  • ...is a bad idea to subclass built-in types in Python - 为什么,我已经创建了一些有用的子类。
  • 对于早期版本的 Python,我记得一些关于子类化内置类型的在线警告,可能是因为随着语言的发展,API 兼容性没有任何保证。我不确定今天的一般做法是什么。
  • 那是很久很久以前的事了,可能在 Python 2.2 中的类/类型统一之前。
猜你喜欢
  • 2013-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
  • 1970-01-01
  • 2020-11-29
  • 2010-10-29
相关资源
最近更新 更多