For how to explain the results of your various attemps, see Matthew's anser.
Between the lines of your question, though, I read that you want to replicate the behavior of a[::-1]
, but with explicitly passing the start
and stop
of the slice.
The Python tutorial's first closer look at strings mentions this useful mnemonic:
One way to remember how slices work is to think of the indices as
pointing between characters, with the left edge of the first
character numbered 0. Then the right edge of the last character of a
string of n characters has index n, for example:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
The first row of numbers gives the position of the indices 0...6 in
the string; the second row gives the corresponding negative indices.
The slice from i to j consists of all characters between the edges
labeled i and j, respectively.
Of course, the same slicing rules apply to other sequences, like lists and tuples. What the tutorial does not mention[1], is that for negative steps, you have to work with shifted indices-pointing-between-the-items, like so:
┎───┰───┰───┰───┰───┰───┒
┃ P ┃ y ┃ t ┃ h ┃ o ┃ n ┃ <== For backwards slicing (i.e. negative step)
┞───╀───╀───╀───╀───╀───┦
0 1 2 3 4 5
-7 -6 -5 -4 -3 -2 -1
This is because indices don't actually point between sequence items. Rather, the stop
parameter of slicing is an exclusive bound, i.e., the resulting sequence will end with the last item covered by step
before the one with index stop
.
From this shifted pseudo-index table we can see, that to have the whole sequence reversed by slicing with explicit bounds, we must start at len(a) - 1
(or above). We further see, that there is no non-negative index we can use as stop
and still have the first item of the original sequence included as the last item of the resulting sequence. [len(a)-1:0:-1]
would end at the second original item already. And we can't use -1
as stop
, as that is the short-hand to refer to the position after the last item independent of len(a)
. We have to use -len(a) - 1
as stop
(or below) instead:
>>> a[len(a)-1:-len(a)-1:-1]
[5, 4, 3, 2, 1]
If one is specifying the bounds explicitly, it's probably so they can be adapted when in a later version of the code only a part of the sequence shall be used. For code to be adapted, the current revision needs to be understood, so one can make an informed decision on what to change. Needless to say, a[len(a)-1:-len(a)-1:-1]
is much less intelligible than list(reversed(a))
, which can easily be augmented with bounds:
>>> list(reversed(a[2:4])) # referring to indices of original sequence
[4, 3]
>>> list(reversed(a))[1:3] # referring to indices of reversed sequence
[4, 3]
[1] The reason why the tutorial doesn't mention that, is probably that until Python 2.2, the possibility of using a step when slicing could only be used for NumPy sequences (and maybe other third-party sequences), but wasn't supported by Python's built-in sequences list, tuple and string. Python 2.3 added the implementation for this 'extended slicing' semantics for the built-in sequence types.