[Python-ideas] Where did we go wrong with negative stride?

Ron Adam ron3200 at gmail.com
Wed Oct 30 04:09:56 CET 2013



On 10/29/2013 05:07 PM, Nick Coghlan wrote:
>
> On 30 Oct 2013 07:26, "Ron Adam"
> <ron3200 at gmail.com

>  > I think it may be the only way to get a clean model of slicing from both
> directions with a 0 based index system.
>
> Isn't all that is needed to prevent the default wraparound behaviour
> clamping negative numbers to zero on input?

Well, you can't have indexing from the other end and clamp index's to zero 
at the same time.

The three situations are...

        index both i and j from the front
        index both i and j from the end
        index i from the front + index j from the end.   (?)

And then there is this one, which is what confuses everyone.

        index i from the end + index j from the front.

It's useful in the current slice semantics where I and J are swapped if K 
is negative.  It works, but is not easy to think about clearly.  It's meant 
to match up with start and stop concepts, rather than left and right.

Another issue is weather or not a slice should raise an Index error if it's 
range is outside the length of the sequence.  Both behaviours are useful. 
Currently it doesn't on one end and give the wrong output on the other. 
(When moving a slice left or right.)   :-/

For that matter the wraparound behaviour is sometimes useful too.  But not 
if it's only on the left side.

And then there's the idea of open a closed ends.  Which you have an 
interest in.  Assuming their is four combinations of that... both-closed, 
left-open, right-open, and both-open.



That's a lot of things to be trying to shove into one syntax!



So it seems (to me) we may be better off to just concentrate on writing 
some functions with the desired behaviour(s) and leaving the slice syntax 
question to later.  (But I'm very glad these things are being addressed.)


A function that would cover nearly all of the use cases I can think of...

     # Get items that are within slice range.
     # index's:  l, r, rl, rr --> left, right, rev-left, rev-right
     # The index's always use positive numbers.
     # step and width can be either positive or negative.
     # width - chunk to take at each step.  (If it can work cleanly.)

     get_slice(obj, l=None, r=None, ri=None, rr=None, step=1, width=1)


Used as...

     a = get_slice(s, l=i, r=j)      # index from left end

     a = get_slice(s, rl=i, rr=j)    # index from right end

     a = get_slice(s, l=i, rr=j)     # index from both ends


While that signature definition is long and not too pretty, it could be 
wrapped to make more specialised and nicer to use variations.  Or it could 
be hidden away in __getitem__ methods.

     def mid_slice(obj, i, j, k):
          """Slice obj i and j distance from ends."""
          return get_slice(obj, l=i, rr=j, step=k)


Instead of using flags for these...

        "closed" "open" "open-right" "open-left" "reversed"
        "raise-err"  "wrap-around"

Would it be possible to have those applied with a context manager while the 
object is being indexed?

      with index_mode(seq, "open", "reversed") as s:
          r = mid_slice(s, i, j)

That could work with any slice syntax we use later.  And makes a nice 
building block for creating specialised slice functions.


> As in:
>
> def clampleft(start, stop, step):
>      if start is not None and start < 0:
>          start = 0
>      if stop is not None and stop < 0:
>          stop = 0
>      return slice(start, stop, step)
>
> Similar to rslice and "reverse=False", this could be implemented as a
> "range=False" flag (the rationale for the flag name is that in "range",
> negative numbers are just negative numbers, without the wraparound
> behaviour normally exhibited by the indices calculation in slice objects).

I know some have mentioned unifying range and slice, even though they 
aren't the same thing...  But it suggests doing...

         seq[range(i,j,k)]

I'm not sure there an any real advantage to that other than testing that 
range and slice behave in similar ways.


> I think there are two reasonable options that could conceivably be included
> in 3.4 at this late stage:
>
> * Make slice subclassable and ensure the C API and stdlib respect an
> overridden indices() method

I think that would be good, it would allow some experimentation that may be 
helpful.  Is there any reason to not allow it?


> * add a "reverse" flag to both slice and range, and a "range" flag to slice.
>
> Either way, if any changes are going to be made, a PEP should be written up
> summarising some of the ideas in this thread, including the clampleft() and
> rslice() recipes that work in current versions of Python.

I agree. :-)



Cheers,
    Ron







More information about the Python-ideas mailing list