16

Why does this not work (values are not swapped):

lol = ["test","test2"]
lol[lol.index("test")], lol[lol.index("test2")] = lol[lol.index("test2")], lol[lol.index("test")]

But this works (values are swapped):

i1 = lol.index("test")
i2 = lol.index("test2")
lol[i1], lol[i2] = lol[i2], lol[i1]
Chris_Rands
  • 38,994
  • 14
  • 83
  • 119
tinusf
  • 192
  • 1
  • 8

3 Answers3

10

The reason why the first example is not working is because you are calling .index() multiple times, and after each time, the values in the list are changing, so the indices found in the code are not representative of the actual locations of the elements. The second example works because you have stored the first indices in two variables, and use both in the swap.

Overview of first example:

lol[lol.index("test")], lol[lol.index("test2")] = lol[lol.index("test2")], lol[lol.index("test")] 

First part: lol[lol.index("test")] stores 0

Second part: lol[lol.index("test2")] stores 1

Third part: lol[lol.index("test2")] still stores 1

This is when it gets interesting. The forth part of the example, lol[lol.index("test")], finds the index of test, however, test was assigned 1 from the third segment of the code. Therefore, lol[lol.index("test")] is 1, not 0. Consequently, lol[lol.index("test2")] still stores 1.

Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • 7
    This gives the wrong impression that in a statement of `A,B=C,D` the evaluation order is **1.** A ; **2.** B ; **3.** C ; **4.** A=C ; **5.** D ; **6.** B=D. While the order of evaluation should be **1.** C ; **2.** D ; **3.** A ; **4.** B ; **5.** A=C ; **6.** B=D – kaza Sep 20 '17 at 15:18
  • This answer below is clearer: https://stackoverflow.com/a/46325562/3559330 – mattyb Apr 19 '21 at 20:23
5

Explanation

It all comes down to understand properly how the evaluation order works here, in particular the case expr3, expr4 = expr1, expr2.

If we step through the statement lol[lol.index("test")], lol[lol.index("test2")] = lol[lol.index("test2")], lol[lol.index("test")] we'd get something like this:

r1=evaluate(expr1) --> "test2"
r2=evaluate(expr2) --> "test"
evaluate(expr3)=r1 --> lol[0] = "test2" --> lol = ["test2","test2"]
evaluate(expr4)=r2 --> lol[0] = "test"  --> lol = ["test", "test2"]

The other snippet is trivial:

i1 = lol.index("test")
i2 = lol.index("test2")
lol[i1], lol[i2] = lol[i2], lol[i1]

it1) i1 = 0
it2) i2 = 1
it3) lol[i1], lol[i2] = "test2", lol[i1]
it4) lol[i1], lol[i2] = "test2", "test"
it5) lol[i1] = "test2"
it6) lol[i2] = "test"

Oneliner alternatives

Something like these ones should do:

lol = lol[lol.index("test2")], lol[lol.index("test")]

lol[0], lol[1] = lol[1], lol[0]

lol[0], lol[1] = lol[lol.index("test2")], lol[lol.index("test")]

Aditional notes

If you really want to know more about how these functions are really interpreted, a very good way to do so is by using the module dis, for example:

>>> import dis
>>> def f():
...    lst[lst.index(str1)], lst[lst.index(str2)] = lst[lst.index(str2)], lst[lst.index(str1)]
...
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (lst)
              3 LOAD_GLOBAL              0 (lst)
              6 LOAD_ATTR                1 (index)
              9 LOAD_GLOBAL              2 (str2)
             12 CALL_FUNCTION            1
             15 BINARY_SUBSCR
             16 LOAD_GLOBAL              0 (lst)
             19 LOAD_GLOBAL              0 (lst)
             22 LOAD_ATTR                1 (index)
             25 LOAD_GLOBAL              3 (str1)
             28 CALL_FUNCTION            1
             31 BINARY_SUBSCR
             32 ROT_TWO
             33 LOAD_GLOBAL              0 (lst)
             36 LOAD_GLOBAL              0 (lst)
             39 LOAD_ATTR                1 (index)
             42 LOAD_GLOBAL              3 (str1)
             45 CALL_FUNCTION            1
             48 STORE_SUBSCR
             49 LOAD_GLOBAL              0 (lst)
             52 LOAD_GLOBAL              0 (lst)
             55 LOAD_ATTR                1 (index)
             58 LOAD_GLOBAL              2 (str2)
             61 CALL_FUNCTION            1
             64 STORE_SUBSCR
             65 LOAD_CONST               0 (None)
             68 RETURN_VALUE
>>>
BPL
  • 9,632
  • 9
  • 59
  • 117
1

Because X,Y="test","test2" will get handled as X="test";Y="test2"

lol = ["test","test2"]
lol[lol.index("test")], lol[lol.index("test2")] = lol[lol.index("test2")], lol[lol.index("test")]

First the right handed side will get evaluated, so you get:

lol[lol.index("test")], lol[lol.index("test2")] = "test2", "test"

which will have the same effect as the following lines:

lol[lol.index("test")]="test2"
#   returns 0
# lol==["test2","test2"]
lol[lol.index("test2")]="test"
#  returns 0
MegaIng
  • 7,361
  • 1
  • 22
  • 35
  • 1
    Although it is obvious can you state that RHS is evaluated first. As some might assume that the statements are split first and then evaluated later. – kaza Sep 20 '17 at 15:13
  • @bulbus agreed, in fact I think the first line is rather misleading as it implies the order for these 2 operations is the same, which is not true of course – Chris_Rands Sep 20 '17 at 15:32