Why can't I swap two items in a list in one line? Why can't I swap two items in a list in one line? python python

Why can't I swap two items in a list in one line?


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 = 0it2) i2 = 1it3) 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