eterey
eterey

Reputation: 420

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?

Upvotes: 5

Views: 1125

Answers (4)

jonrsharpe
jonrsharpe

Reputation: 122032

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'

Upvotes: 8

nmichaels
nmichaels

Reputation: 50951

If you want to override operators like +, you'll need to do it explicitly. The documentation will help you figure out what needs to change.

Upvotes: 2

dg99
dg99

Reputation: 5663

Operations such as *=, +=, and del are implemented using the list class's operator functions like __add__, __delitem__, __delslice__, etc. There are lots of them. If you want to intercept every possible call to your "extended" list class, you'll need to override most if not all of those operators.

Because you don't have control over how the builtin classes are implemented, it's usually easier to compose a new class around a builtin than to inherit from a builtin.

Upvotes: 4

User
User

Reputation: 14873

Have a look at the class UserList: http://pydoc.org/2.2.3/UserList.html It shows all methods you need to change. A problem is that type(ExtendedList([]) * 2) == list.

Upvotes: 1

Related Questions