How to subclass list and trigger an event whenever the data change?
<p>I would like to subclass <code>list</code> and trigger an event (data checking) every time any change happens to the data. Here is an example subclass:</p>

<pre><code>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.")
</code></pre>

<p>Here, I am overriding methods <code>__init__</code>, <code>__setitem__</code> and <code>__append__</code> 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 <em>any</em> kind of mutation happens to the underlying data structure?</p>

<p>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.</p>

<p>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 <a href="https://docs.python.org/library/collections.abc.html" rel="nofollow"><code>collections.abc</code></a> module. You have to implement only a minimum amount of methods and the module automatically implements the rest for you.</p>

<p>For your specific example, this would be something like this:</p>

<pre><code>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
</code></pre>

<h3>Performance</h3>

<p>Some methods are slow in their default implementation. For example <code>__contains__</code> is defined in the <a href="https://hg.python.org/cpython/file/tip/Lib/_collections_abc.py#l806" rel="nofollow"><code>Sequence</code> class</a> as follows:</p>

<pre><code>def __contains__(self, value):
    for v in self:
        if v is value or v == value:
            return True
    return False
</code></pre>

<p>Depending on your class, you might be able to implement this faster. However, performance is often less important than writing code which is easy to understand. It can also make writing a class harder, because you're then responsible for implementing the methods correctly.</p>

python