13

I want to swap the minimum and maximum element in a list. My code is as follows:

A=[5,1,3,4,2]

a=max(A)
b=min(A)

A[A.index(a)],A[A.index(b)]=A[A.index(b)],A[A.index(a)]

print(A)  #prints [5,1,3,4,2]

But, when I write:

A[0],A[1]=A[1],A[0]

print(A) #prints [1,5,3,4,2]

As you can see, in the second case, it shows the correct output. Why not in the first case?

1 Answers1

15

If we look at the instructions the compiler generates using the dissasembler:

>>> import dis
>>> dis.dis("A[A.index(a)],A[A.index(b)]=A[A.index(b)],A[A.index(a)]")
  1           0 LOAD_NAME                0 (A)
              2 LOAD_NAME                0 (A)
              4 LOAD_METHOD              1 (index)
              6 LOAD_NAME                2 (b)
              8 CALL_METHOD              1
             10 BINARY_SUBSCR
             12 LOAD_NAME                0 (A)
             14 LOAD_NAME                0 (A)
             16 LOAD_METHOD              1 (index)
             18 LOAD_NAME                3 (a)
             20 CALL_METHOD              1
             22 BINARY_SUBSCR
             24 ROT_TWO
             26 LOAD_NAME                0 (A)
             28 LOAD_NAME                0 (A)
             30 LOAD_METHOD              1 (index)
             32 LOAD_NAME                3 (a)
             34 CALL_METHOD              1
             36 STORE_SUBSCR
             38 LOAD_NAME                0 (A)
             40 LOAD_NAME                0 (A)
             42 LOAD_METHOD              1 (index)
             44 LOAD_NAME                2 (b)
             46 CALL_METHOD              1
             48 STORE_SUBSCR
             50 LOAD_CONST               0 (None)
             52 RETURN_VALUE
>>>

So, first, it evaluates A[A.index(b)],A[A.index(a)], we can think of the partially evaluated statement as:

A[A.index(a)], A[A.index(b)]= 1, 5

Then, as you can see, the first part

A[0] = 1

Gets done first, i.e.

         26 LOAD_NAME                0 (A)
         28 LOAD_NAME                0 (A)
         30 LOAD_METHOD              1 (index)
         32 LOAD_NAME                3 (a)
         34 CALL_METHOD              1
         36 STORE_SUBSCR

So the list has:

[1, 1, 3, 4, 2]

Then, the final part,

             38 LOAD_NAME                0 (A)
             40 LOAD_NAME                0 (A)
             42 LOAD_METHOD              1 (index)
             44 LOAD_NAME                2 (b)
             46 CALL_METHOD              1
             48 STORE_SUBSCR

But at this point, A.index(b) is 0! So it does:

A[0] = 5

And you end up where you started. The problem is, you modified where A.index(b) would end up, since it happens from left-to-right in order.

Here is a reference to the documentation for assignment statements in the language spec. I guess the important thing to note is that the right-hand side is evaluated first, then the parts of the left-hand side (potentially separated by commas) are evaluated one by one in order.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • 3
    If you think your answer is useful you can adapt it for the linked duplicates too. (I can see that none of the answers in the existing duplicates that I can find uses `dis`. Teaching people to use `dis` to understand what happens is not a bad idea, but looking for the info in the language specification is better if you can) – user202729 Feb 22 '21 at 03:45
  • @user202729 yes, I'm trying to find something in the language spec that clarifies. – juanpa.arrivillaga Feb 22 '21 at 03:48