```
>>arr = [4, 2, 1, 3]
>>arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0]
>>arr
```

Result I expect
`>>[3, 2, 1, 4]`

Result I get
`>>[3, 2, 4, 3]`

Basically I'm trying to swap the #4 and #3 (In my actual problem, the index wont be 0, but rather an iterator "i" . So I cant just do arr[0], arr[3] = arr[3], arr[0]) I thought I understood simultaneous assignment fairly well. Apparently I was mistaken. I don't understand why arr[arr[0]-1] on the left side of the assignment is evaluating to arr[2] instead of arr[3]. If the assignments happen simultaneously (evaluated from the right),

arr[0] (within the index of the 2nd element on the left)should still be "4"

arr[0] -1 (the index of the 2nd element on the left) should thus be "3"

·
Santiago Trujillo

Because the target list does not get evaluated *simultaneously*. Here is the relevant section of the docs:

The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

Two things to keep in mind, the right hand side evaluates the expression first. So on the RHS, we first create the tuple :

```
(3, 4)
```

Note, that is done *left to right*. Now, the assignment to each target in the target list on the left is done in order:

```
arr[0] = 3
```

Then the next target, `arr[0]`

is * 3*, and 3-1 is 2

```
arr[2] = 4
```

So a simple solution is to just to compute the indices first before the swap:

```
>>> arr = [4, 2, 1, 3]
>>> i, j = arr[0] - 1, 0
>>> arr[j], arr[i] = arr[i], arr[j]
>>> arr
[3, 2, 1, 4]
```

Here is a demonstration using a verbose list that we can define easily:

```
>>> class NoisyList(list):
... def __getitem__(self, item):
... value = super().__getitem__(item)
... print("GETTING", item, "value of", value)
... return value
... def __setitem__(self, item, value):
... print("SETTING", item, 'with', value)
... super().__setitem__(item, value)
...
>>> arr = NoisyList([4, 2, 1, 3])
>>> arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0]
GETTING 0 value of 4
GETTING 3 value of 3
GETTING 0 value of 4
SETTING 0 with 3
GETTING 0 value of 3
SETTING 2 with 4
```

·
Santiago Trujillo
Report

Ok, this happens because the `arr[0]`

is changed to `3`

when you assign `arr[arr[0]-1]`

to it. And after that, when python takes a look at `arr[arr[0]-1]`

and tries to assign it the value of `arr[0]`

if finds `arr[0]`

to be `3`

because in the previous assignment you have changed it. A small demonstration:

```
arr = [4, 2, 1, 3]
arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0]
│ │
└──────────┬────────────────┘
│
│
these are done first
and the list becomes:
[3, 2, 1, 3]
Next when the python takes a look at
these two, it:
│
┌────────────┴───────────────┐
│ │
arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0]
it finds the `arr[0]` to be `3` so, it assigns `3` to the `3rd`
element because `arr[arr[0]-1]` is 3.
```

·
Santiago Trujillo
Report

The replacement of the two values isn't truly simultaneous; they are handled in order from left to right. So altering `arr`

during that process is leading to this behavior.

Consider this alternative example:

```
>>> arr = [1, 2, 3]
>>> arr[0], arr[arr[0]] = 10, 5
...
```

With a hypothetical simultaneous reassignment, we try to replace the first value of `arr`

with `10`

, and then the `arr[0]`

*th* (aka `1`

*st*) element with `5`

. So *hopefully*, we get `[10, 5, 3]`

. But this fails with `IndexError: list assignment index out of range`

. If you then inspect `arr`

after this error:

```
>>> arr
[10, 2, 3]
```

The first assignment was completed, but the second failed. When it came to the second assignment (after the comma), the actual `arr[0]`

*th* (aka `10`

*th*) value cannot be found (b/c the list isn't that long).

This behavior can also be seen by clearly specifying the second assignment to fail (still by specifying an index out of range):

```
>>> arr = [1, 2, 3]
>>> arr[0], arr[99] = 5, 6
# Same index error, but arr becomes [5, 2, 3]
```

This feels reminiscent of modifying a list you are iterating over, which is sometimes doable but often discouraged because it leads to issues like what you are seeing.

One alternative is to create a copy (this is sometimes a solution for modifying the list you are iterating over), and use that for referencing values in `arr`

:

```
>>> arr = [4, 2, 1, 3]
>>> copy = arr.copy()
>>> arr[0], arr[copy[0]-1] = copy[copy[0]-1], copy[0]
>>> arr
[3, 2, 1, 4]
```

Though it is pretty ugly here. The alternative in the accepted answer is much nicer, or this idiomatic approach should probably work as well!

·
Santiago Trujillo
Report