Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement hooks for list & dict changes

My app relies on list and dict data structures for maintaining current state. Now I need to track whenever element is added/removed from list or dict data changes. I've found out that there are collections.abc.Sequence (for list) and collections.abc.MutableMapping (for dict), but they're very limited and result can't be used instead of list/dict (append, clear, ...).

I've been thinking about some proxy class that'll forward calls and provide hooks to be called before/after some method is forwarded, but didn't find anything that even looks like in.

How do I hook mutators of given structures? Is there something I don't know?

like image 573
Daniel Avatar asked Oct 21 '25 14:10

Daniel


2 Answers

Even though subclassing built-in classes is officially supported, I'd still recommend you consider using a proxy class.

The problem is it isn't well documented when overriden methods are called. For instance, if you override __setitem__ method, it won't be called when you modify your dict using extend method, e.g. mydict.extend({"a": 42}). You'll have to override extend, too. It's very easy to forget to override some obscure method, and modification will occur silently without your callback getting called (whereas if you were using a proxy, it would throw an AttributeError indicating that you forgot to define some method).

There's even more to that. Some built-in methods that receive a dictionary through argument will not call any overriden methods at all in CPython, as shown in the following example from PyPy wiki:

>>>> class D(dict):
....     def __getitem__(self, key):
....         return 42
....
>>>>
>>>> d1 = {}
>>>> d2 = D(a='foo')
>>>> d1.update(d2)
>>>> print d1['a']
foo # but 42 in PyPy
like image 63
WGH Avatar answered Oct 23 '25 07:10

WGH


Subclassing dict maybe:

class DictWatch(dict):
    def __init__(self, *args, **kwargs):
        self.callback = kwargs.pop('callback')
        dict.__init__(self, args)

    def __setitem__(self, key, val):
        # YOUR HOOK HERE
        self.callback(key, val)
        dict.__setitem__(self, key, val)

    # and other overrided dict methods if you need them

Demo:

>>> def cb(k,v):
...     print k,v
... 
>>> d=DictWatch(callback=cb)
>>> d['key']='100'
key 100
like image 38
ndpu Avatar answered Oct 23 '25 06:10

ndpu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!