pythonlistinheritance

Right way to extend list


I want to extend functionality of class "list" and add custom handlers for events: "Add new item to list" and "Remove item from list". For this task I don't want to use composition, inheritance is better.

I tried do:

class ExtendedList(list):
    
    def append(self, obj):
        super(ExtendedList, self).append(obj)
        print('Added new item')
    
    def extend(self, collection):
        if (hasattr(collection, '__iter__') or hasattr(collection, '__getitem__')) and len(collection)>0:
            for item in collection:
                self.append(item)
    
    def insert(self, index, obj):
        super(ExtendedList, self).insert(index, obj)
        print('Added new item')
    
    def remove(self, value):
        super(ExtendedList, self).remove(value)
        print('Item removed')

but it doesn't work correctly. I can't catch all adding and removing events. For example:

collection = ExtendedList()
collection.append('First item')
# Out: "Added new item\n"; collection now is: ['First item']
collection.extend(['Second item', 'Third item'])
# Out: "Added new item\nAdded new item\n"; collection now is: ['First item', 'Second item', 'Third item']
collection += ['Four item']
# Doesn't output anything; collection now is: ['First item', 'Second item', 'Third item', 'Four item']
collection.remove('First item')
# Out: "Item removed\n"; collection now is: ['Second item', 'Third item', 'Four item']
del collection[0:2]
# Doesn't output anything; collection now is: ['Four item']
collection *= 3
# Doesn't output anything; collection now is: ['Four item', 'Four item', 'Four item']

What is the right way to extend class "list" for my situation?


Solution

  • Rather than inherit from list itself, inherit from its Abstract Base Class, collections.abc.MutableSequence. This does all of the basic work for you, letting you focus on what you want to change. There is a good question on ABCs here: Why use Abstract Base Classes in Python?

    For example:

    from collections.abc import MutableSequence
    
    
    class ExtendedList(MutableSequence):
    
        def __init__(self, logger=print):
            self._content = []
            self._logger = logger
    
        def __getitem__(self, index):
            return self._content[index]
    
        def __setitem__(self, index, item):
            self._content[index] = item
    
        def __delitem__(self, index):
            del self._content[index]
            self._logger("item removed")
    
        def __len__(self):
            return len(self._content)
    
        def insert(self, index, item):
            self._content.insert(index, item)
            self._logger("item added")
    

    In use (still not quite what you want, but closer):

    >>> collection = ExtendedList()
    >>> collection.append("First item")
    item added
    >>> collection.extend(["Second item", "third item"])
    item added
    item added
    >>> collection += ["Fourth item"]
    item added
    >>> collection.remove("First item")
    item removed
    >>> del collection[0:2]
    item removed
    >>> collection *= 3
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for *=: 'ExtendedList' and 'int'