List Callbacks?
Solution 1:
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]
defcallback_method(func):
defnotify(self,*args,**kwargs):
for _,callback in self._callbacks:
callback()
return func(self,*args,**kwargs)
return notify
classNotifyList(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):
ifisinstance(item,slice):
return self.__class__(list.__getitem__(self,item))
else:
returnlist.__getitem__(self,item)
def__init__(self,*args):
list.__init__(self,*args)
self._callbacks = []
self._callback_cntr = 0defregister_callback(self,cb):
self._callbacks.append((self._callback_cntr,cb))
self._callback_cntr += 1return self._callback_cntr - 1defunregister_callback(self,cbid):
for idx,(i,cb) inenumerate(self._callbacks):
if i == cbid:
self._callbacks.pop(idx)
return cb
else:
returnNoneif __name__ == '__main__':
A = NotifyList(range(10))
defcb():
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)defcb2():
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 ...
Solution 2:
You'd have to subclass list
and modify __setitem__
.
classNotifyingList(list):def__init__(self, *args, **kwargs):
self.on_change_callbacks = []
def__setitem__(self, index, value):
for callback inself.on_change_callbacks:
callback(self, index, value)
super(NotifyingList, self).__setitem__(name, index)
notifying_list = NotifyingList()
defprint_change(list_, index, value):
print 'Changing index %d to %s' % (index, value)
notifying_list.on_change_callbacks.append(print_change)
As noted in comments, it's more than just __setitem__
.
You might even be better served by building an object that implements the list
interface and dynamically adds and removes descriptors to and from itself in place of the normal list machinery. Then you can reduce your callback calls to just the descriptor's __get__
, __set__
, and __delete__
.
Solution 3:
I'm almost certain this can't be done with the standard list.
I think the cleanest way would be to write your own class to do this (perhaps inheriting from list
).
Post a Comment for "List Callbacks?"