Skip to content Skip to sidebar Skip to footer

List Callbacks?

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' >

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?"