Dominik Dominik - 3 months ago 16
Python Question

How to subclass list and trigger an event whenever the data change?

I would like to subclass

list
and trigger an event (data checking) every time any change happens to the data. Here is an example subclass:

class MyList(list):

def __init__(self, sequence):
super().__init__(sequence)
self._test()

def __setitem__(self, key, value):
super().__setitem__(key, value)
self._test()

def append(self, value):
super().append(value)
self._test()

def _test(self):
""" Some kind of check on the data. """
if not self == sorted(self):
raise ValueError("List not sorted.")


Here, I am overriding methods
__init__
,
__setitem__
and
__append__
to perform the check if data changes. I think this approach is undesirable, so my question is: Is there a possibilty of triggering data checking automatically if any kind of mutation happens to the underlying data structure?

Answer

As you say, this is not the best way to go about it. To correctly implement this, you'd need to know about every method that can change the list.

The way to go is to implement your own list (or rather a mutable sequence). The best way to do this is to use the abstract base classes from Python which you find in the collections.abc module. You have to implement only a minimum amount of methods and the module automatically implements the rest for you.

For your specific example, this would be something like this:

from collections.abc import MutableSequence

class MyList(MutableSequence):

    def __init__(self, iterable=()):
        self._list = list(iterable)

    def __getitem__(self, key):
        return self._list.__getitem__(key)

    def __setitem__(self, key, item):
        self._list.__setitem__(key, item)
        # trigger change handler

    def __delitem__(self, key):
        self._list.__delitem__(key)
        # trigger change handler

    def __len__(self):
        return self._list.__len__()

    def insert(self, index, item):
        self._list.insert(index, item)
        # trigger change handler

Performance:

The performance is probably quite good, single method lookups should be fast. However, if you really wanted, you could directly assign the self._list's method to the MyListclass.

Comments