[Python-ideas] Where did we go wrong with negative stride?
Andrew Barnert
abarnert at yahoo.com
Thu Oct 31 08:18:33 CET 2013
This reminded me of something related.
Quasi-sequences—things that implement the implicit sequence protocol (being indexable with contiguous integers starting from 0, so they're good enough to be used as iterables even though they don't define __iter__), but aren't Sequences (e.g., because they're lazy and/or infinite and therefore can't be Sized) work with slices (as long as start and stop are nonnegative). But only because they ignore the indices method and use the start, stop, and step attributes directly. And that will break any meaningful subclass of slice (except those that only deviate from the base class when given negative indices).
One option is to allow these quasi-sequences to call indices(None), which (in slice; subclasses could do something different if they wanted) would raise an IndexError if its start or stop were negative, otherwise act as if it were given an infinite length. (This would also make such quasi-sequences easier to write, and more consistent.)
Here's an example (which may be kind of silly, but someone wrote it, and it works, and it's in a project I maintain…): a LazyList that wraps up an iterator and acts like a quasi-sequence—you can index it and slice it, and even mutate it; the first time you try to get/set/del an index higher than all that have been accessed so far, it moves an appropriate number of values from the stored iterator to a list, then just does the get/set/del on that list.
For example, if Squares(i) is an iterator that's like (n*n for n in itertools.count(i)) but with a useful repr:
>>> ll = LazyList(Squares(0))
>>> ll
LazyList(Squares(0))
>>> ll[1:-1]
IndexError: LazyList indices cannot be negative
>>> ll[1]
1
>>> ll
LazyList(0, 1, Squares(2))
>>> del ll[2:6:2]
>>> ll
LazyList(0, 1, 9, Squares(5))
>>> ll[5:2:-1]
[49, 36, 25]
>>> ll
LazyList(0, 1, 9, 25, 36, 49, Squares(8))
For an even simpler example, here's an InfiniteRange class:
>>> r = InfiniteRange(2)
>>> r
InfiniteRange(2)
>>> r[0]
2
>>> r[11::2]
InfiniteRange(13, 2)
>>> r[11:15:2]
range(13, 17, 2)
>>> r[15:11:-2]
range(17, 13, -2)
>>> r[:-1]
IndexError: InfiniteRange indices cannot be negative
----- Original Message -----
> From: Andrew Barnert <abarnert at yahoo.com>
> To: Terry Reedy <tjreedy at udel.edu>
> Cc: "python-ideas at python.org" <python-ideas at python.org>
> Sent: Wednesday, October 30, 2013 11:06 PM
> Subject: Re: [Python-ideas] Where did we go wrong with negative stride?
>
> On Oct 30, 2013, at 15:47, Terry Reedy <tjreedy at udel.edu> wrote:
>
>> I think one point is that if seq.__getitem__(ob) uses 'if
> isinstance(ob, slice):' instead of 'if type(ob) is slice:', subclass
> instances will work whereas wrapper instances would not.
>
> Why would anyone use isinstance(ob, slice)? If you can write "start, stop,
> step = ob.indices(length) without getting a TypeError, what else do you care
> about? (Code that _does_ care about the difference probably wouldn't work
> correctly with any custom slice object anyway.)
>
> If there really is an issue, we could easily add a collections.abc.Slice.
>
> Or... This may be a heretical and/or just stupid idea, but what about reviving
> the old names __getslice__ and friends (now taking a slice object instead of
> 2.x's start and stop)? Then the interpreter calls __getslice__ if you use
> slicing syntax, or if you use indexing syntax with an instance of abc.Slice (or
> anything but a numbers.Number even?). That way the code is only in one place
> instead of having to be written in each sequence class.
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
>
More information about the Python-ideas
mailing list