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
 >>>

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to