Look at the bytecode for the statement in question:
>>> def f(A):
... A[0], A[A[0]] = A[A[0]], A[0]
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (A)
3 LOAD_FAST 0 (A)
6 LOAD_CONST 1 (0)
9 BINARY_SUBSCR
10 BINARY_SUBSCR
11 LOAD_FAST 0 (A)
14 LOAD_CONST 1 (0)
17 BINARY_SUBSCR
18 ROT_TWO
19 LOAD_FAST 0 (A)
22 LOAD_CONST 1 (0)
25 STORE_SUBSCR
26 LOAD_FAST 0 (A)
29 LOAD_FAST 0 (A)
32 LOAD_CONST 1 (0)
35 BINARY_SUBSCR
36 STORE_SUBSCR
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
This works as follows:
- Instructions 0-10 push A [A [0]] onto the value stack, so this
2 - Instructions 11-17 push A [0] into the value stack, now this
2, 1 - Instruction 18 swaps the stack
1, 2 - Instructions 19–25 assign the upper value (2) A [0], leaving the value stack as
1 - Instructions 26-36 will try to assign the value A [A [0]], but A [0] is now 2, so it tries to assign A [2], which is an IndexError.
, , A[A[0]] , A[0].
:
A0 = A[0]
A[0], A[A0] = A[A0], A[0]