ListMixin (WAS: How do you control _all_ items added to a list?)
Michael Spencer
mahs at telcopartners.com
Tue Mar 1 15:21:39 EST 2005
Steven Bethard wrote:
> Nick Coghlan wrote:
> > Hmm, it might be nice if there was a UserList.ListMixin that was the
> > counterpart to UserDict.DictMixin
>
> I've thought this occasionally too. One of the tricky issues though is
> that often you'd like to define __getitem__ for single items and have
> ListMixin add the code for slices. I haven't figured out how to do this
> cleanly yet...
>
> STeVe
I agree that would be useful. One solution would be to ask users to implement
__getsingleitem__ (and not __getitem__) if they want the mixin to handle slice
logic. The following illustrates that, and also falls back to slicing the
iterator if it is provided:
class ProtoListMixin(object):
"""Prototype ListMixin, exploring slice interface and semantics"""
def __getitem__(self, index):
if isinstance(index, slice):
start, stop, step = index.start or 0, index.stop, index.step or 1
if start < 0 or stop < 0 or not stop:
try:
start, stop, step = index.indices(len(self))
except TypeError:
raise TypeError, "unsized object"
try:
getter = self.__getsingleitem__
return [getter(i) for i in range(start, stop, step)]
except AttributeError:
pass
else:
if index < 0:
try:
index = len(self) + index
except TypeError:
raise TypeError, "unsized object"
try:
return self.__getsingleitem__(index)
except AttributeError:
pass
start, stop, step = index, index + 1, None
# Alternatively, try to use the iterator, if available
import itertools
try:
args = [iter(self)]
except AttributeError:
raise TypeError, "Must implement __getsingleitem__ or __iter__"
if start:
args.append(start)
args.append(stop)
if step:
if step < 1:
raise ValueError, "slicing an iterable requires step >=1"
args.append(step)
iterator = itertools.islice(*args)
if isinstance(index, slice):
return list(iterator)
else:
try:
return iterator.next()
except StopIteration:
raise IndexError, "index out of range"
# Users should implement __getsingleitem__ for positive indices
class Index(ProtoListMixin):
def __init__(self, data):
"""For testing, provide a list"""
self._data = data
def __getsingleitem__(self, index):
return self._data[index]
# If __len__ is implemented, negative indices are supported
class IndexLen(Index):
def __len__(self):
return len(self._data)
# If __getsingleitem__ is not implemented, positive slices are returned
# from an iterator
class Iter(ProtoListMixin):
def __init__(self, data):
"""For testing, provide an iterable"""
self._data = data
def __iter__(self):
return iter(self._data)
>>> a = Index(range(10))
>>> a[4]
4
>>> a[4:8]
[4, 5, 6, 7]
>>> a[-4]
Traceback (most recent call last):
File "<input>", line 1, in ?
File "ListMixin", line 22, in __getitem__
TypeError: unsized object
>>> b = IndexLen(range(10))
>>> b[-4]
6
>>> c = Iter(xrange(10))
>>> c[3]
3
>>> c[3:6]
[3, 4, 5]
>>> c[-3]
Traceback (most recent call last):
File "<input>", line 1, in ?
File "ListMixin", line 22, in __getitem__
TypeError: unsized object
>>>
More information about the Python-list
mailing list