Reputation: 420
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
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
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
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
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