问题描述
每次修改列表时,是否有任何方法使列表
调用函数?
例如:
>>> l = [1,2,3]
>> ; def callback():
printlist changed
>>> apply_callback(l,callback)
>>> l.append(4)
更改的列表
>>> l [0] = 5
更改列表
& >>> l.pop(0)
列表已更改
5
借用@ sr2222的建议,这里是我的尝试。 (我将使用没有语法糖的装饰器):
import sys
pre>
_pyversion = sys .version_info [0]
def callback_method(func):
def notify(self,* args,** kwargs):
for _,callback in self._callbacks:
callback()
return func(self,* args,** kwargs)
return notify
class NotifyList(list):
extend = callback_method list.extend)
append = callback_method(list.append)
remove = callback_method(list.remove)
pop = callback_method(list.pop)
__delitem__ = callback_method __delitem__)
__setitem__ = callback_method(list .__ setitem__)
__iadd__ = callback_method(list .__ iadd__)
__imul__ = callback_method(list .__ imul__)
#返回一个新的NotifyList如果我们切片。
if _pyversion< 3:
__setslice__ = callback_method(list .__ setslice__)
__delslice__ = callback_method(list .__ delslice__)
def __getslice __(self,* args):
return self .__ class __(list。 __getslice __(self,* args))
def __getitem __(self,item):
if isinstance(item,slice):
return self .__ class __(list .__ getitem __ ,item))
else:
return list .__ getitem __(self,item)
def __init __(self,* args):
list .__ init __(self, * args)
self._callbacks = []
self._callback_cntr = 0
def register_callback(self,cb):
self._callbacks.append ._callback_cntr,cb))
self._callback_cntr + = 1
return self._callback_cntr - 1
def unregister_callback(self,cbid):
for idx, i,cb)in enumerate(self._callbacks):
if i == cbid:
self._callbacks.pop(idx)
return cb
else:
返回无
如果__name__ =='__main__':
A = NotifyList(range(10))
def cb():
print Modify!)
#register a callback
cbid = A.register_callback(cb)
A.append('Foo')
+ = [1,2,3]
A * = 3
A [1:2] = [5]
del A [1:2]
#Add另一个回调。它们将按顺序调用(最早的)
def cb2():
print(Modify2)
A.register_callback(cb2)
print 80)
A [5] ='baz'
print( - * 80)
#unregister第一个回调
A.unregister_callback(cbid)
A [5] ='qux'
打印( - * 80)
打印(A)
打印:3]))
print(type(A [1:3:2]))
print(type(A [5]))
这是伟大的事情,如果你意识到你忘了考虑一个特定的方法,它只是一行代码添加它。 (例如,我刚才忘了
__ iadd __
和__ imul __
:
EDIT
我已将代码稍微更新为py2k和py3k兼容。此外,切片创建一个与父类型相同类型的新对象。请随时在这个食谱继续戳洞,所以我可以让它更好。这实际上看起来像一个非常整洁的事情有手头...
Is there any way to make a
list
call a function every time the list is modified?For example:
>>>l = [1, 2, 3] >>>def callback(): print "list changed" >>>apply_callback(l, callback) # Possible? >>>l.append(4) list changed >>>l[0] = 5 list changed >>>l.pop(0) list changed 5
解决方案Borrowing from the suggestion by @sr2222, here's my attempt. (I'll use a decorator without the syntactic sugar):
import sys _pyversion = sys.version_info[0] def callback_method(func): def notify(self,*args,**kwargs): for _,callback in self._callbacks: callback() return func(self,*args,**kwargs) return notify class NotifyList(list): extend = callback_method(list.extend) append = callback_method(list.append) remove = callback_method(list.remove) pop = callback_method(list.pop) __delitem__ = callback_method(list.__delitem__) __setitem__ = callback_method(list.__setitem__) __iadd__ = callback_method(list.__iadd__) __imul__ = callback_method(list.__imul__) #Take care to return a new NotifyList if we slice it. if _pyversion < 3: __setslice__ = callback_method(list.__setslice__) __delslice__ = callback_method(list.__delslice__) def __getslice__(self,*args): return self.__class__(list.__getslice__(self,*args)) def __getitem__(self,item): if isinstance(item,slice): return self.__class__(list.__getitem__(self,item)) else: return list.__getitem__(self,item) def __init__(self,*args): list.__init__(self,*args) self._callbacks = [] self._callback_cntr = 0 def register_callback(self,cb): self._callbacks.append((self._callback_cntr,cb)) self._callback_cntr += 1 return self._callback_cntr - 1 def unregister_callback(self,cbid): for idx,(i,cb) in enumerate(self._callbacks): if i == cbid: self._callbacks.pop(idx) return cb else: return None if __name__ == '__main__': A = NotifyList(range(10)) def cb(): print ("Modify!") #register a callback cbid = A.register_callback(cb) A.append('Foo') A += [1,2,3] A *= 3 A[1:2] = [5] del A[1:2] #Add another callback. They'll be called in order (oldest first) def cb2(): print ("Modify2") A.register_callback(cb2) print ("-"*80) A[5] = 'baz' print ("-"*80) #unregister the first callback A.unregister_callback(cbid) A[5] = 'qux' print ("-"*80) print (A) print (type(A[1:3])) print (type(A[1:3:2])) print (type(A[5]))
The great thing about this is if you realize you forgot to consider a particular method, it's just 1 line of code to add it. (For example, I forgot
__iadd__
and__imul__
until just now :)EDIT
I've updated the code slightly to be py2k and py3k compatible. Additionally, slicing creates a new object of the same type as the parent. Please feel free to continue poking holes in this recipe so I can make it better. This actually seems like a pretty neat thing to have on hand ...
这篇关于列表回调?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!