In Python sequences such as lists, tuples and strings, individual elements can be accessed by indexing. Typically each element have an index relative to other elements in the list, where the indices starts at 0(the first element) and ends at the length of the sequence minus one.
In addition to the positive indexing, Python also supports negative indexing. So literally, any element in a Python sequence has two valid indices i.e the positive one and its negative counterpart. This allows us to conveniently access elements with whichever index gives us the most natural access to the element we want.
In negative indexing, the indices starts from the end of a sequence moving to the front. The last element in the sequence is given the index -1
, the second-last element is given the index -2
, and so on until we reach the front of the list where the first element in the list has an index equal to -n
, where n is the length of the sequence.
As shown in the above example, the last element in the list has an index of -1
, and the indices of the rest elements decreases by one as you move to the front of the list.
Just like on positive indexing, if the index is not in the valid index range, an IndexError
is raised. This is for example if the negative index is smaller than the smallest valid index which is equivalent to -len(lst)
.
In the above example, an IndexError
is raised because we used -10 as the index where it is not valid since the tuple has only 4 elements and thus the smallest valid index is -4
.
Assignment with negative indexing
When the sequence that we are dealing with is mutable such as a list, we can use negative indexing to perform assignment operations on the list. This happens when we override an element in a certain posltion in the list with a new value.
The assignment operation is not possible with tuples or strings because they are immutable.
Slicing with negative indexes
Slicing is an operation that is closely related to indexing in that it allows you to select a subset of data from a sequence data type. In other words indexing allows us to access a single elements in the sequences while slicing allows us to access multiple elements in a sequence in a single statement.
The syntax of slicing is as follows:
seq[start:stop:step]
start |
The starting index of the slice. |
stop |
Determines the end of the slice. The stop index is not included on the the slice! |
step | Determines the step size of how many elements to jump over when slicing. |
We can use negative slicing to access the elements from back instead of from front.
In the above example, we specified a slice of seq[-2 : -5: -1] meaning that the returned sequence will have elements starting at the second to last element of the sequence (index -2), up to but not including the seventh to last element of the sequence (index -7), with a step size of -1 (so, going backwards).