23
votes

Here is a snippet of code which gives the output: 0 1 2 2. I had expected the output 3 3 3 3 since a[-1] accesses the number 3 in the list. The explanation given online says "The value of a[-1] changes in each iteration" but I don't quite understand how or why. Any explanations would be great!

a = [0, 1, 2, 3]
for a[-1] in a:
    print(a[-1])
3
I have never seen anything like that; I'd love to read the article / tutorial / whatever it is that shows this example, could you please post link to it? - GingerPlusPlus
Also, congratulations for nice first question! (faith in new SO users restored) - GingerPlusPlus
Nice question. Why is a[-1] even allowed as the loop variable... - timgeb
Straight-up funniest for-loop I've ever seen. - Eli Rose

3 Answers

17
votes

What's happening here is a list is mutated during looping.

Let's consider following code snippet:

a = [0, 1, 2, 3]
for a[-1] in a:
    print a

Output is:

[0, 1, 2, 0]
[0, 1, 2, 1]
[0, 1, 2, 2]
[0, 1, 2, 2]

Each iteration:

  • reads value from position currently pointed by internal pointer
  • immediately assigns it to last element in list
  • after that last element is printed on standard output

So it goes like:

  • internal pointer points to first element, it's 0, and last element is overwritten with that value; list is [0, 1, 2, 0]; printed value is 0
  • internal pointer points to second element, it's 1, and last element is overwritten with that value; list is [0, 1, 2, 1]; printed value is 1
  • (...)
  • at last step, internal pointer points to last element; last element is overwritten by itself - list does not change on last iteration; printed element also does not change.
36
votes

While doing for a[-1] in a, you actually iterate through the list and temporary store the value of the current element into a[-1].

You can see the loop like these instructions:

a[-1] = a[0] # a = [0, 1, 2, 0]
print(a[-1]) # 0
a[-1] = a[1] # a = [0, 1, 2, 1]
print(a[-1]) # 1
a[-1] = a[2] # a = [0, 1, 2, 2]
print(a[-1]) # 2
a[-1] = a[3] # a = [0, 1, 2, 2]
print(a[-1]) # 2

So, when you are on the third element, then 2 is stored to a[-1] (which value is 1, but was 0 before and 3 on start).

Finally, when it comes to the last element (and the end of the iteration), the last value stored into a[-1] is 2 which explains why it is printed twice.

0
votes

This code

a = [0, 1, 2, 3]
for a[-1] in a:
    print(a[-1])

is equivalent to

a = [0, 1, 2, 3]
for i in range( len(a) ):
    a[-1] = a[i] # update last element of list(updated) with element at i'th position
    print(a[-1]) # print last element of list

the output will be 0 1 2 2

Explanation:

len(a) = 4, range( len(a) ) = [0, 1, 2, 3]

  • 1st loop, current list = [0, 1, 2, 3], i = 0 => a[i] = a[0] = 0, updated list = [0, 1, 2, 0], last element a[-1] = 0
  • 2nd loop, current list = [0, 1, 2, 0], i = 1 => a[i] = a[1] = 1, updated list = [0, 1, 2, 1], last element a[-1] = 1
  • 3rd loop, current list = [0, 1, 2, 1], i = 2 => a[i] = a[2] = 2, updated list = [0, 1, 2, 2], last element a[-1] = 2
  • 4th loop, current list = [0, 1, 2, 2], i = 3 => a[i] = a[3] = 2, updated list = [0, 1, 2, 2], last element a[-1] = 2