List slicing with negative index
Negative slicing Python
String Slicing with Negatives in Python - Stack Overflow
python - Understanding negative steps in list slicing - Stack Overflow
Videos
Yes, calling s[0:-1] is exactly the same as calling s[:-1].
Using a negative number as an index in python returns the nth element from the right-hand side of the list (as opposed to the usual left-hand side).
so if you have a list as so:
myList = ['a', 'b', 'c', 'd', 'e']
print myList[-1] # prints 'e'
the print statement will print "e".
Once you understand that (which you may already, it's not entirely clear if that's one of the things you're confused about or not) we can start talking about slicing.
I'm going to assume you understand the basics of a slice along the lines of myList[2:4] (which will return ['c', 'd']) and jump straight into the slicing notation where one side is left blank.
As you suspected in your post, myList[:index] is exactly the same as myList[0:index].
This is also works the other way around, by the way... myList[index:] is the same as myList[index:len(myList)] and will return a list of all the elements from the list starting at index and going till the end (e.g. print myList[2:] will print ['c', 'd', 'e']).
As a third note, you can even do print myList[:] where no index is indicated, which will basically return a copy of the entire list (equivalent to myList[0:len(myList)], returns ['a', 'b', 'c', 'd', 'e']). This might be useful if you think myList is going to change at some point but you want to keep a copy of it in its current state.
If you're not already doing it I find just messing around in a Python interpreter a whole bunch a big help towards understanding these things. I recommend IPython.
>>> l = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz&']
# I want a string up to 'def' from 'vwx', all in between
# from 'vwx' so -2;to 'def' just before 'abc' so -9; backwards all so -1.
>>> l[-2:-9:-1]
['vwx', 'stu', 'pqr', 'mno', 'jkl', 'ghi', 'def']
# For the same 'vwx' 7 to 'def' just before 'abc' 0, backwards all -1
>>> l[7:0:-1]
['vwx', 'stu', 'pqr', 'mno', 'jkl', 'ghi', 'def']
Please do not become listless about list.
- Write the first element first. You can use positive or negative index for that. I am lazy so I use positive, one stroke less (below 7, or -3 for the start).
- Index of the element just before where you want to stop. Again, you can use positive or negative index for that (below 2 or -8 for stop).
Here sign matters; of course - for backwards; value of stride you know. Stride is a 'vector' with both magnitude and direction (below -1, backwards all).
l = [0,1,2,3,4,5,6,7,8,9] l[7:2:-1], l[-3:2:-1], [-3:-8:-1],l[7:-8:-1]All result in
[7, 6, 5, 4, 3].
Although I know how slicing works but following examples kind of stumped me.
values = [3,4,3,7,8,9,5] values[:3:-1] #o/p [5,9,8] values[5:3:-1] #o/p [9,8]
I always thought in list[start:stop:step], 'start' defaults to 0, 'stop' defaults to length-1 and 'step' defaults to 1. But this doesn't make sense here. What are the rules? Link to official docs will also be appreciated as I couldn't find that.
You should use in reverse order
a[-4:-1]
# gjo
you should actually use
a[-3:]
# job
if you want to reverse the string
a[-1:-4:-1]
# boj
Slice notation is like this:
a[start:stop:step]
This gets the elements from index start up to (but not including) index stop incremented by step. By default step is 1, so you get an empty string since your start is already bigger than your stop.
Our first step is to fix the order of the indexes:
>>> a[-4:-1]
'gjo'
But this still isn't quite right. For negative indexing, the last character is at index -1, the next to the last at -2, etc. The best way to think about this is to get the last n characters of the string, you need to start at index -n. So let's starting at -3 to get the last three characters:
>>> a[-3:-1]
'jo'
But we are still missing the last character. This is because the last index is not included in the slice. The last fix is to leave out the last index:
>>> a[-3:]
'job'
Leaving out the last index tells python to slice the string until the end of the string.
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]