Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding negative steps in list slicing

I am trying to understand the following behavior and would welcome any references (especially to official docs) or comments.

Let's consider a list:

>>> x = [1,2,3,4,5,6]

This works as expected

>>> x[-1:-4:-1] 
[6, 5, 4]

But I am surprised the following is empty:

>>>  x[0:-4:-1] 
[]

Consequently, I am surprised the following is not empty

>>> x[0:-len(x)-1:-1]
> [1]

especially given that

>>> x[0:-len(x):-1] 
[]

and also that

>>> x[0:-len(x)-1] 
[]

is empty.

like image 525
jarm Avatar asked Jan 21 '17 19:01

jarm


People also ask

What happens when you use negative numbers in slicing notation?

Negative Slicing in Python Similar to negative indexing, Python also supports negative slicing. This means you can start slicing from the end of the sequence. Here the slicing starts at the index -4 which is the 4th last element of the list. The slicing ends at the last item, which is at the index -1.

What does a negative step size mean in Python range slicing?

A negative step size indicates that we are not slicing from left to right, but from right to left. Hence, the start index should be larger or equal than the end index (otherwise, the resulting sequence is empty).

What is the use of negative indices in slicing in Python?

Negative Indexing is used to in Python to begin slicing from the end of the string i.e. the last. Slicing in Python gets a sub-string from a string. The slicing range is set as parameters i.e. start, stop and step.

How does negative step work in Python?

The step valueWhenever a negative step value is given, the default meaning of start and stop change. The start value will now default to the length of the list and the stop value will default to just before the beginning of the list.


2 Answers

I was pointed to the reference implementation (hattip to the Anonymous Benefactor) and found that it is fairly straightforward to understand the behavior from there. To be complete, IMHO this behavior is unintuitive, but it nevertheless is well defined and matches the reference implementation.

Two CPython files are relevant, namely the ones describing list_subscript and PySlice_AdjustIndices. When retrieving a slice from a list as in this case, list_subscript is called. It calls PySlice_GetIndicesEx, which in turn calls PySlice_AdjustIndices. Now PySlice_AdjustIndices contains simple if/then statements, which adjust the indices. In the end it returns the length of the slice. To our case, the lines

if (*stop < 0) {
    *stop += length;
    if (*stop < 0) {
        *stop = (step < 0) ? -1 : 0;
    }
}

are of particular relevance. After the adjustment, x[0:-len(x)-1:-1] becomes x[0:-1:-1] and the length 1 is returned. However, when x[0:-1:-1] is passed to adjust, it becomes x[0:len(x)-1:-1] of length 0. In other words, f(x) != f(f(x)) in this case.

It is amusing to note that there is the following comment in PySlice_AdjustIndices:

/* this is harder to get right than you might think */

Finally, note that the handing of the situation in question is not described in the python docs.

like image 106
jarm Avatar answered Sep 24 '22 01:09

jarm


The fact that

> x[-1:-4:-1] 
[6, 5, 4]
> x[0:-4:-1] 
[]

should not surprise you! It is fairly obvious that you can slice a list from the last to the fourth-last element in backwards steps, but not from the first element.

In

x[0:i:-1]

the i must be < -len(x) in order to resolve to an index < 0 for the result to contain an element. The syntax of slice is simple that way:

x[start:end:step]

means, the slice starts at start (here: 0) and ends before end (or the index referenced by any negative end). -len(x) resolves to 0, ergo a slice starting at 0 and ending at 0 is of length 0, contains no elements. -len(x)-1, however, will resolve to the actual -1, resulting in a slice of length 1 starting at 0.

Leaving end empty in a backward slice is more intuitively understood:

> l[2::-1]
[3, 2, 1]
> l[0::-1]
[1]
like image 41
user2390182 Avatar answered Sep 26 '22 01:09

user2390182