Python list error: [::-1] step on [:-1] slice
The first -1
in a[:-1:-1]
doesn't mean what you think it does.
In slicing, negative start/end indices are not interpreted literally. Instead, they are used to conveniently refer to the end of the list (i.e. they are relative to len(a)
). This happens irrespectively of the direction of the slicing.
This means that
a[:-1:-1]
is equivalent to
a[:len(a)-1:-1]
When omitted during reverse slicing, the start index defaults to len(a)-1
, making the above equivalent to
a[len(a)-1:len(a)-1:-1]
This always gives an empty list, since the start and end indices are the same and the end index is exclusive.
To slice in reverse up to, and including, the zeroth element you can use any of the following notations:
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:None:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-len(a)-1:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
I generally find it useful to slice a range
-object (this is only possible in python3 - in python2 range
produces a list
and xrange
can't be sliced) if I need to see which indices are used for a list of a given length:
>>> range(10)[::-1]
range(9, -1, -1)
>>> range(10)[:-1]
range(0, 9)
And in your last case:
>>> range(10)[:-1:-1]
range(9, 9, -1)
This also explains what happened. The first index is 9, but 9 isn't lower than the stop index 9 (note that in python the stop index is excluded) so it stops without giving any element.
Note that indexing can also be applied sequentially:
>>> list(range(10))[::-1][:-1] # first reverse then exclude last item.
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(10))[:-1][::-1] # other way around
[8, 7, 6, 5, 4, 3, 2, 1, 0]
When you type [1, 2, 3, ...][1:4:1]
it is the same as [1, 2, 3, ...][slice(1, 4, 1)]
. So 1:4:1
is the shorthand for slice
object. slice
signature is slice(stop)
or slice(start, stop[, step])
and you can also use None
for arguments.
:: -> slice(None, None, None)
:4 -> slice(4)
# and so on
Suppose we have got [a: b: c]
. Rules for indices will be as follows:
- First
c
is checked. Default is+1
, sign ofc
indicates forward or backward direction of the step. Absolute value ofc
indicates the step size. - Than
a
is checked. Whenc
is positive orNone
, default fora
is0
. Whenc
is negative, default fora
is-1
. - Finally
b
is checked. Whenc
is positive orNone
, default forb
islen
. Whenc
is negative default forb
is-(len+1)
.
Note 1: Degenerated slices in Python are handled gracefully:
- the index that is too large or too small is replaced with
len
or0
. - an upper bound smaller than the lower bound returns an empty list or string or whatever else (for positive
c
).
Note 2: Roughly speaking, Python picks up elements while this condition (a < b) if (c > 0) else (a > b)
is True
(updating a += c
on every step). Also, all negative indices are replaced with len - index
.
If you combine this rules and notes it will make sense why you got an empty list. In your case:
In[1]: [1, 2, 3, 4, 5, 6][:-1:-1] # `c` is negative so `a` is -1 and `b` is -1
Out[1]: []
# it is the same as:
In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1] # which will produce you an empty list
Out[2]: []
There is very good discussion about slice notation: Explain Python's slice notation!