question about slicing with a step length

André andre.roberge at gmail.com
Thu Mar 9 07:15:10 EST 2006


Terry Reedy wrote:
> > John Salerno wrote:
> >> Given:
> > > numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> > > can someone explain to me why
> > > numbers[10:0:-2] results in [10, 8, 6, 4, 2]?
>
> It appears that s[i:j:-1] is s[(j+1):(i+1)] .reverse()'ed.  For 'numbers',
> this is 10, 9, 8, 7, 6, 5, 4, 3, 2].  Then take every other item.  Why the
> +1?  Don't know and not my intuitive expectation.  I just know that
> extended slicing was developed for and until recently pretty much
> restricted to numeric (now numpy).
>
It's not simply "+1".
>>> a = range(10)
>>> a[9:0:-2]
[9, 7, 5, 3, 1]

>>> a[10:0:-2]
[9, 7, 5, 3, 1]

>>> a[11:0:-2]
[9, 7, 5, 3, 1]

>>> a[42:0:-2]
[9, 7, 5, 3, 1]

> Steven D'Aprano
> > I think the documentation is misleading/incomplete when
> > it comes to negative strides for extended slices.
>
> and Andre "Agreed!" also, and you three aren't the only ones.  Maybe some
> day I will read the source, think about it more, and post a suggested
> revision for comment  ... or maybe someone will beat me to it.

I, myself, think that the slicing behaviour is broken in this case.
>From the little I understand (without looking at the source),
adjustment on the boundaries of the slicing "request" (the first 2
numbers) is done first, to make them positive and, I believe, ensure
that they do not extend beyond the boundaries of the sequence.  Then,
the actual stepping through is performed.

In my (most likely not-well informed enough opinion), the behaviour
should be the following:
from [i: j: k],
if k > 0 and j <= i : return []
if k< 0 and j >= i : return []
otherwise, build the following list of numbers:
i, i+k, i+2*k,
until i +n*k >= j (if k>0; use <= if k<0)
where the last number is excluded, i.e.
[0: 2: 1] = list(0, 1)
*Then*, exclude from the list just built any negative numbers, or any
number greater than the length of the sequence to be sliced.

So, [10:0:-2] would yield the following list of numbers
[10, 8, 6, 4, 2] before trimming;  applying this to
range(10), would end up with result in trimming "10" from the list
built.
Thus, we are looking at indices [8, 6, 4, 2] of range(10), which happen
to be the numbers [8, 6, 4, 2] in this case.  We should end up with
the same list if we ask for [42:0:-2].   This is not the observed
behaviour,
as we get [9, 7, 5, 3, 1].
If we ask Python to do
>>> a[0:10:2]
we get
[0, 2, 4, 6, 8]
which is correct.  It seems to me that the following (pseudo-code, not
valid Python)
a[2:10:2] == a[8:0:-2].reversed()
should be true, just like
a[1:10:1] == a[9:0:-1].reversed()
is true.

I *suspect* that "boundary adjustments" are performed first before
creating a sequence of indices, so that there is less "trimming" (as
described above) for the sake of efficiency ... and that this is not
done properly.

Then again, perhaps I simply don't understand what slices are supposed
to do....

André

> 
> Terry Jan Reedy




More information about the Python-list mailing list