It starts counting from "2" going backwards (step -1) until "-5" is reached - but reaching the element "-5" would require positive step in this case.
For example the output of:
i[2::-1]
is:
[2, 1, 0]
It starts counting from "2" going backwards (step -1) until "-5" is reached - but reaching the element "-5" would require positive step in this case.
For example the output of:
i[2::-1]
is:
[2, 1, 0]
As it turns out, when going from left to right (using negative or positive version of index), a positive step size is required.
Going from right to left requires a negative step size:
i = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]
# to get 6, 7, 8
print(i[6:9])
print(i[-4:-1:1])
print(i[6:-1:1])
print(i[-4:9:1])
# to get 8, 7, 6
print(i[8:5:-1])
print(i[-2:-5:-1])
print(i[-2:5:-1])
print(i[8:-5:-1])
Python range() with negative strides - Stack Overflow
python - understanding negative slice step value - Stack Overflow
What's the logic behind stop value of range function with negative step value in python - Stack Overflow
python - Understanding negative steps in list slicing - Stack Overflow
You can specify the stride (including a negative stride) as the third argument, so
range(10,-11,-1)
gives
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
In general, it doesn't cost anything to try. You can simply type this into the interpreter and see what it does.
This is all documented here as:
range(start, stop[, step])
but mostly I'd like to encourage you to play around and see what happens. As you can see, your intuition was spot on.
Yes, by defining a step:
for i in range(10, -11, -1):
print(i)
The reason why f[0:5:-1] does not generate any output is because you are starting at 0, and trying to count backwards to 5. This is impossible, so Python returns an empty string.
Instead, you want f[5:0:-1], which returns the string "raboo".
Notice that the string does not contain the f character. To do that, you'd want f[5::-1], which returns the string "raboof".
You also asked:
I have read start should not pass stop. is that true in case of negative step value also?
No, it's not true. Normally, the start value shouldn't pass the stop value, but only if the step is positive. If the step is negative, then the reverse is true. The start must, by necessity, be higher then the stop value.
You can think like that:
with f[0:5], 0 is the start position and 5-1 the end position.
with f[0:5:-1], 5 is the start position and 0+1 the end position.
In slicing the start position must be lower than the end position.
When this is not the case the result is an empty string. Thus f[0:5:-1] returns an empty string.
The python docs for range(start, stop[, step]) says the following: :
For a negative step, the contents of the range are still determined by the formula r[i] = >start + step*i, but the constraints are i >= 0 and r[i] > stop.
This means the stop argument isn't included in the range calculation.
range() excludes the stop point when generating your list (well actually they return a generator). You should change 3 to 2, then it will include 3
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.
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]
[-1:0:-1] means: start from the index len(string)-1 and move up to 0(not included) and take a step of -1(reverse).
So, the following indexes are fetched:
le-1, le-1-1, le-1-1-1 .... 1 # le is len(string)
example:
In [24]: strs = 'foobar'
In [25]: le = len(strs)
In [26]: strs[-1:0:-1] # the first -1 is equivalent to len(strs)-1
Out[26]: 'raboo'
In [27]: strs[le-1:0:-1]
Out[27]: 'raboo'
The Python documentation (here's the technical one; the explanation for range() is a bit easier to understand) is more correct than the simplified "every kth element" explanation. The slicing parameters are aptly named
slice[start:stop:step]
so the slice starts at the location defined by start, stops before the location stop is reached, and moves from one position to the next by step items.