10

I found this code snippet to be very interesting.

a = [0, 1, 2, 3]

for a[-1] in a:
    print(a)

Output is as follows:

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

I am trying to understand why python does that. Is it because python is trying to re-use the index? For loop somehow slices the list?

We can add or delete an element while iterating the list, but when we are trying to access the variable using index, it gives bizarre output.

Can someone help me understand the interaction between for loop and index in the list? Or simply explain this output?

Ondrej K.
  • 8,841
  • 11
  • 24
  • 39
Rishi P Bhatt
  • 115
  • 1
  • 8
  • 1
    Interesting question. I'd try to find a better title though. It's very generic, and would you actually consider it an "issue"? – Carcigenicate Jul 22 '18 at 00:07
  • 2
    Imagine `for x in a: a[-1] = x` – Ry- Jul 22 '18 at 00:07
  • At first I thought the code couldn't work, then I thought that the last output should have a three on the back. Softening. Thanks for asking. – qräbnö Jul 22 '18 at 00:25
  • It's only the `a[-1]` that makes this moderately interesting. The equivalent `for a[0] in a` makes it perfectly clear there are no slices, no unexpected interaction, and no bizarre output. It's also more clear that only one element ever changes. – Jongware Jul 22 '18 at 00:31

3 Answers3

8

It works as expected. (For some interpretation of "expected", at least.)

Re-writing your code to this, to prevent any misinterpretation of what a[-1] is at any point:

a = [a for a in range(0,4)]
for b in a:
    print (b)
    a[-1] = b
    print (a)

shows us

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

which makes it clear that the b assignment to a[-1] is done immediately, changing the list while iterating.

The four loops do the following:

  1. a[-1] gets set to the first value of the list, 0. The result is now [0,1,2,0].
  2. a[-1] gets set to the second value, 1. The result is (quite obviously) [0,1,2,1].
  3. a[-1] gets set to 2 and so the result is [0,1,2,2] – again, only a[-1] gets changed.
  4. Finally, a[-1] gets set to the last value in a, so effectively it does not change and the final result is [0,1,2,2].
Jongware
  • 22,200
  • 8
  • 54
  • 100
1

a[-1] prints the same value in a[3], the last index in the list. Edit: see comment below for the explanation for this.

The result reads as follows:

[0, 1, 2, 0] -> same as [0, 1, 2, 3], but with a[i] (in this case, a[-1] == a[3]) is replaced with the value at a[0]
[0, 1, 2, 1] -> [0, 1, 2, 3] but a[i] is replaced with the value at a[1]
[0, 1, 2, 2] -> [0, 1, 2, 3] but a[i] is replaced with the value at a[2]
[0, 1, 2, 2] -> [0, 1, 2, 3] but a[i] is replaced with the value at a[3] from the previous iteration.
jshamble
  • 391
  • 2
  • 14
  • 2
    "It probably has an index out of bounds in the negative direction": no, this is [perfectly ordinary Python syntax](https://stackoverflow.com/q/11367902/2564301). It is mentioned in [the official documentation](https://docs.python.org/3/library/stdtypes.html?highlight=negative#common-sequence-operations) under #3. – Jongware Jul 22 '18 at 00:39
0

What happens is

         0 SETUP_LOOP              24 (to 26)
          2 LOAD_GLOBAL              0 (a)
          4 GET_ITER
    >>    6 FOR_ITER                16 (to 24)
          8 LOAD_GLOBAL              0 (a)     //ARRAY (TOS1)
         10 LOAD_CONST               2 (-1)    //DEST (TOS)
         12 STORE_SUBSCR                       //ARRAY[DEST] = TOS2*
        14 LOAD_GLOBAL              1 (print)
         16 LOAD_GLOBAL              0 (a)
         18 CALL_FUNCTION            1
         20 POP_TOP
         22 JUMP_ABSOLUTE            6
    >>   24 POP_BLOCK
    >>   26 LOAD_CONST               0 (None)
         28 RETURN_VALUE

*So if someone could clarify that TOS2 is actually the 'visited' value of the ARRAY?

PolishCivil
  • 131
  • 2
  • 13