Steven Bethard wrote:
Nick Coghlan wrote: > Hmm, it might be nice if there was a UserList.ListMi xin that was the
> counterpart to UserDict.DictMi xin
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):
"""Prototyp e ListMixin, exploring slice interface and semantics"""
def __getitem__(sel f, index):
if isinstance(inde x, 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(l en(self))
except TypeError:
raise TypeError, "unsized object"
try:
getter = self.__getsingl eitem__
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.__getsingl eitem__(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(sta rt)
args.append(sto p)
if step:
if step < 1:
raise ValueError, "slicing an iterable requires step >=1"
args.append(ste p)
iterator = itertools.islic e(*args)
if isinstance(inde x, 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(ProtoList Mixin):
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(ProtoListM ixin):
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