Why does reversing a list using slice notation with 0 as "stop" not return the entire list?

Question:

In the following example:

foo = ['red', 'white', 'blue', 1, 2, 3]

foo[0:6:1] will print all elements in foo. However, foo[6:0:i-1] will omit the 1st or 0th element.

>>> foo[6:0:-1]
[3, 2, 1, 'blue', 'white']

I understand that I can use foo.reverse() or foo[::-1] to print the list in reverse, but why doesn’t foo[6:0:-1] print the entire list?

Asked By: user737079

||

Answers:

Slice notation in short:

[ <first element to include> : <first element to exclude> : <step> ]

If you want to include the first element when reversing a list, leave the middle element empty, like this:

foo[::-1]

You can also find some good information about Python slices in general here:
Explain Python's slice notation

Answered By: Andrew Clark

…why foo[6:0:-1] doesn’t print the entire list?

Because the middle value is the exclusive, rather than inclusive, stop value. The interval notation is [start, stop).

This is exactly how [x]range works:

>>> range(6, 0, -1)
[6, 5, 4, 3, 2, 1]

Those are the indices that get included in your resulting list, and they don’t include 0 for the first item.

>>> range(6, -1, -1)
[6, 5, 4, 3, 2, 1, 0]

Another way to look at it is:

>>> L = ['red', 'white', 'blue', 1, 2, 3]
>>> L[0:6:1]
['red', 'white', 'blue', 1, 2, 3]
>>> len(L)
6
>>> L[5]
3
>>> L[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

The index 6 is beyond (one-past, precisely) the valid indices for L, so excluding it from the range as the excluded stop value:

>>> range(0, 6, 1)
[0, 1, 2, 3, 4, 5]

Still gives you indices for each item in the list.

Answered By: Fred Nurk

This answer might be a little outdated, but it could be helpful for someone who stuck with same problem.
You can get reverse list with an arbitrary end – up to 0 index, applying second in-place slice like this:

>>> L = list(range(10))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> (start_ex, end) = (7, 0)
>>> L[end:start_ex][::-1]
[6, 5, 4, 3, 2, 1, 0]
Answered By: Emreu

If you are having trouble remembering slice notation, you could try doing the Hokey Cokey:

[In: Out: Shake it all about]

[First element to include: First element to leave out: The step to use]

YMMV

Answered By: Captain Lepton

Use

>>>foo[::-1]

This displays the reverse of the list from the end element to the start,

Answered By: Ashmita Dutta

Complement. to reverse step by 2:

A = [1,2,2,3,3]
n = len(A)
res = [None] * n
mid = n//2 + 1 if n%2 == 1 else n//2

res[0::2] = A[0:mid][::-1]
res[1::2] = A[0:mid][::-1]
print(res)

[2, 3, 2, 3, 1]

Answered By: Charlie 木匠

You can get it to work if you use a negative stop value. Try this:

foo[-1:-7:-1]
Answered By: myQwil

formalizing the answer from andrew-clark a little bit more:

Suppose the list v and v[n1:n2:n3] slice.
n1 is initial position, n2 is final position and n3 is step

Let’s write some pseucode in a Python way:

n3 = 1  if (n3 is missing) else n3
if n3==0:
   raise exception # error, undefined step

First part: n3 positive

if n3>0:
   slice direction is from left to right, the most common direction         

   n1 is left slice position in `v` 
   if n1 is missing: 
      n1 = 0   # initial position
   if n1>=0:
      n1 is a normal position
   else: 
     (-n1-1) is the position in the list from right to left 

   n2 is right slice position in `v` 
   if n2 is missing: 
      n2 = len(x)  # after final position
   if n2>=0:
      n2 is a normal final position (exclusive)
   else: 
      -n2-1 é the final position in the list from right to left 
       (exclusive)

Second part: n3 negative

else: 
  slice direction is from right to left (inverse direction)

  n1 is right slice position in `v` 
  if n1 is missing: 
     n1 = -1   # final position is last position in the list.
  if n1>=0:
     n1 is a normal position
  else: 
     (-n1-1) is the position in the list from right to left 

  n2 is  left slice position in `v` 
  if n2 is missing: 
     n2 = -len(x)-1   # before 1st character  (exclusive)
  if n2>=0:
     n2 is a normal final position (exclusive)
  else: 
     -n2-1 is the ending position in the list from right to left 
     (exclusive)

Now the original problema: How to reverse a list with slice notation?

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(L(::-1)) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Why?
n1 is missing and n3<0 => n1=0
n2 is missing and n3<0 => n2 = -len(x)-1

So L(::-1) == L(-1:-11:-1)

Answered By: Paulo Buchsbaum

This is a very good question, and, in my opinion, none of the above answers really answer the question being asked. Partly because an intellectually honest answer may reveal a weakness of Python type system. And the same weakness exists in some other languages.

A more intellectually honest answer would probably be:
Python slicing does not generalize well.

Precisely, one can traverse a collection in one direction with collection[begin:end] but collection[end:begin:-1] does not work. It does not work because the first index is 0 but the "index before the first index" is not -1. Thus, collection[end:-1:-1] does not evaluate to what we would legitimately expect.

The [first to include : first to exclude : step] explanation is flawed. If "first to exclude" is the one before the first element, you cannot express it the same way you would in the case that you wanted to include that same first element.

In conclusion, you’re not crazy to expect generality, it is just not there.

Answered By: foki
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.