1

Say the list

s = [1, 2]

and I want to use tuple assignments to edit the list. Why it is fine to do

s[s[0]], s[0] = s[0], s[s[0]] # s == [2, 1]

but not

s[0], s[s[0]] = s[s[0]], s[0] # get error, index out of range
qsmy
  • 383
  • 3
  • 14
  • Does this answer your question? [Tuple unpacking order changes values assigned](https://stackoverflow.com/questions/34171348/tuple-unpacking-order-changes-values-assigned) – user202729 Feb 22 '21 at 03:43

3 Answers3

2

A good explanation of what is happening under the hood can be found here. Essentially when you have multiple assignments anything to the right of the = will be fully evaluated first.

Let’s take your first example and walk through what is happening.

s[s[0]], s[0] = s[0], s[s[0]] # s == [2, 1]

This is equivalent to the following where temp values store the evaluated right hand sides

# assigns right hand side to temp variables

temp1 = s[0]    # 1
temp2 = s[s[0]] # 2

# then assign those temp variables to the left-hand side

s[s[0]] = temp1 # s[1] = 1 —-> s= [1, 1]
s[0] = temp2    # s[0] = 2 —-> s = [2, 1]

In the case you get an index error the following happens...

# assigns right hand side to temp variables

temp1 = s[s[0]] # 2
temp2 = s[0]    # 1

# then assign those temp variables to the left-hand side

s[0] = temp1 # s[0] = 2 —-> s= [2, 1]
s[s[0]] = temp2  # s[2] -> Index error (highest index of s is 1)
Dharman
  • 30,962
  • 25
  • 85
  • 135
Abe Pena
  • 51
  • 2
1

When you do s[a], s[b] = s[b], s[a], the right side of the = is evaluated completely before assigning the values back to the list, which is why one-line-swaps work in Python. However, it seems like the left side of the = is not evaluated all at once, but one after another. Thus, for s[0], s[s[0]] = s[s[0]], s[0], the s[0] in s[s[0]] on the left side is evaluated after s[0] was assigned a new value.

This can be verified by wrapping the indices into a function that prints them before returning them:

>>> s = [1, 2]
>>> f = lambda i, j: print("get", i, j) or j
>>> s[f("a",0)], s[f("b",s[f("c",0)])] = s[f("d", s[f("e", 0)])], s[f("f", 0)]
get e 0
get d 1
get f 0
get a 0
get c 0
get b 2  <-- updated to 2 before s[0] was evaluated
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range

In other words, the expression s[0], s[s[0]] = s[s[0]], s[0] seems to be roughly equivalent to the following, i.e. the indices used in the LHS are not evaluated all at once before being assigned new values:

tmp = (s[s[0]], s[0])
s[0] = tmp[0]
s[s[0]] = tmp[1]
tobias_k
  • 81,265
  • 12
  • 120
  • 179
0

lists are assigned by reference, meaning changing the content of the source will result in changing the target assignment.

list = [1,2]
a = [4]
list.append(a) # value is 4

a[0] = 5 # also changes target "list"
print (list) # will output [1,2,[5]]
EvilSmurf
  • 819
  • 1
  • 7
  • 21
  • What do you mean by in order? According to https://openbookproject.net/thinkcs/python/english3e/tuples.html, the tuple on the right side of `=` is evaluated before being assigned to the tuple on the left side – qsmy Aug 06 '20 at 13:05
  • I think it's because they're not actual values, but references. Check the following: list = [1,2] a = [4] list.append(a) a[0] = 5 print (list) you will see the same "behavior" – EvilSmurf Aug 06 '20 at 13:16