python - 为什么使用 setattrib 重载 __setitem__ 不能按预期工作?
问题描述
我正在构建一个简单的可观察列表,当列表改变时触发观察者函数。为了避免对每个方法一遍又一遍地重复几乎相同的代码,我用for
循环和setattr
. 我要装饰的方法存储在methods_to_decorate
. 这是我的班级定义。
class ObservableList(list):
def __init__(self, value):
super().__init__(value)
self.observers = []
self._decorate_methods()
def register_observer(self, callback):
self.observers.append(callback)
def _trigger_observers(self):
for func in self.observers:
func()
def _make_observable(self, func):
"""Decorator that makes a method observable"""
def wrapper(*args, **kwargs):
value = func(*args, **kwargs)
self._trigger_observers()
return value
return wrapper
def _decorate_methods(self):
"""Make methods that change the list observable
using the decorator
"""
methods_to_decorate = [
'__setitem__', '__reversed__', '__delitem__',
'append', 'clear', 'copy', 'extend', 'insert', 'pop', 'remove',
'reverse', 'sort'
]
for attr in methods_to_decorate:
setattr(self, attr, self._make_observable(getattr(self, attr)))
请注意,这__setitem__
是一种装饰方法。这表现如预期,除了切片设置操作:
def my_observer():
print(' -> The list was changed')
lst = ObservableList([1, 2, 3])
lst.register_observer(my_observer)
print('append() should trigger')
lst.append(4) # -> The list was changed
print('pop() should trigger')
lst.pop() # -> The list was changed
print('Slice reading should not trigger')
lst[0] # (no output, expected)
print('Slice writing should trigger')
lst[0] = 0 # (no output, not expected)
设置切片不会触发观察者。但是,如果我显式覆盖__setitem__
(处理切片设置),则对象的行为与预期一样,切片设置也会触发观察者:
# Only if __setitem__ is overloaded explicitly
# the observers are triggered on slice setting.
def __setitem__(self, key, value):
super().__setitem__(key, value)
self._trigger_observers()
为什么重载__setitem__
显式工作,但为什么用它的修饰版本重载它setattr
不起作用?
解决方案
user2357112 的答案支持 Monica 为我指明了找到答案的方向。根据这篇文章,setattr
覆盖对象实例的方法,而显式重载改变了类本身的定义。按照设计,魔术方法(即以双下划线开头的方法)不能在实例级别上被覆盖。因此__setitem__
,其他魔术方法必须在类定义中显式重载。
推荐阅读
- angular - Angular 脚本评估需要很多时间
- dart - 列表视图中的行未显示
- php - 验证失败时,Laravel 重定向到根 url 而不是之前的
- android - 如何从 google play 中删除通话和短信权限?
- sql - SQL Server 2016 - 如果所有其他参数都相同,如何选择数据作为范围
- operating-system - DB2/400 上的STMTHEAP?
- python - 如何在没有 one-hot 编码的情况下将“str”数据提供给决策树
- google-cloud-platform - 通过 gcloud CLI 读取 CloudSQL StackDriver 日志
- rundeck - 如何在 rundeck 中获取执行时间并将其用于另一项工作?
- python - 是否可以用 python 解决运行两个可执行文件并且它们可以相互通信(stdin/stdout)?