On Sun, Oct 27, 2013 at 11:38 AM, Tim Peters <tim.peters@gmail.com> wrote:
I may have a different slant on this.

Hardly -- I agree with everything you say here. :-)
I've found that - by far - the
most successful way to "teach slices" to newcomers is to invite them
to view indices as being _between_ sequence elements.


<position 0> <element> <position 1> <element> <position 2> <element>
<position 3> ...

Then i:j selects the elements between position i and position j.

>>> "abcde"[2:4]

But for negative strides this is all screwed up ;-)

>>> "abcde"[4:2:-1]

Right, that's the point of my post.

They're not getting the elements between "positions" 2 and 4 then,
they're getting the elements between positions 3 and 5.  Why?
"Because that's how it works" - they have to switch from thinking
about positions to thinking about array indexing.

So I would prefer that the i:j in s[i:j:k] _always_ specify the
positions in play:

If i < 0:
    i += len(s)  # same as now
if j < 0:
    j += len(s)  # same as now
if i >= j:
    the slice is empty!  # this is different - the sign of k is irrelevant
    the slice indices selected will be
        i, i + abs(k), i + 2*abs(k), ...
    up to but not including j
    if k is negative, this index sequence will be taken in reverse order

Then "abcde"[4:2:-1] would be "", while "abcde"[2:4:-1] would be "dc",
the reverse of "abcde"[2:4].  And s[0:len(s):-1] would be the same as

Except reversed() returns an iterator. But yes.

This would also make a[i:j:k] == a[i:j][::k].

If I could do it over I would do it this way.

So it's always a semi-open range, inclusive "at the left" and
exclusive "at the right".  But that's more a detail:  the _point_ is
to preserve the mental model of selecting the elements "between
position".  Of course I'd change range() similarly.

Which probably would cause more backward incompatibility bugs, since by now many people have figured out that if you want [4, 3, 2, 1, 0] you have to write range(4, -1, -1). :-(

> For example,
> "abcde"[::-1] == "edcba"
> as you'd expect, but there is no number you can put as the second bound to
> get the same result:

Actually, any integer <= -1-len("abcde") = --6 works.  But, yes,
that's bizarre ;-)

Yup, MRAB pointed this out too.

> Are we stuck with this forever?

Probably :-(

Sadly, I agree. If we wanted to change this in Python 4, we'd probably have to start deprecating range() with negative stride today to force people to replace their uses of that with reversed(range(...)).

--Guido van Rossum (python.org/~guido)