Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simultaneous assignment indexing different list elements in Python [duplicate]

>>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"

like image 709
Sopeade Lanlehin Avatar asked Aug 04 '21 01:08

Sopeade Lanlehin


People also ask

How do I get the index of duplicate items in a list?

Method #1 : Using loop + set() In this, we just insert all the elements in set and then compare each element's existence in actual list. If it's the second occurrence or more, then index is added in result list.

Does Python allow simultaneous assignment statements?

In Python, the simultaneous assignment statement offers an elegant alternative. Here is a simpler Python equivalent: original value of x by assigning to it the value of y? When we then end up with two copies of the original y value.

How do you get multiple values from a list in Python?

In Python, you can return multiple values by simply return them separated by commas. In Python, comma-separated values are considered tuples without parentheses, except where required by syntax. For this reason, the function in the above example returns a tuple with each value as an element.

How does the index function work in Python?

The Python index() method helps you find the index position of an element or an item in a string of characters or a list of items. It spits out the lowest possible index of the specified element in the list. In case the specified item does not exist in the list, a ValueError is returned.


Video Answer


3 Answers

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
like image 67
juanpa.arrivillaga Avatar answered Oct 24 '22 12:10

juanpa.arrivillaga


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 1st) 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 10th) 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!

like image 37
Tom Avatar answered Oct 24 '22 10:10

Tom


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.
like image 2
Parvat . R Avatar answered Oct 24 '22 12:10

Parvat . R