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]
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
.
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
>>>
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