2

I am new to Python and the following question is so difficult for me to understand.

a,b,c=1,2,3
a,b,c=c,a,b=b,a,c=c,b,a
print(a,b,c)

The output of this is -

(2,3,1)

but I don't understand why it isn't -

(3,2,1)
2
  • 1
    (a, b, c) = (c, a, b) = (b, a, c) = (3, 2, 1), and while keeping in mind that statements are evaluated from right to left, is it clearer now? Commented Dec 26, 2019 at 18:24
  • This would be a prefect time for you to use a debugger! debuggers are a great tool especially if you can use them properly. If you're using PyCharm press alt+shift+f9 and use alt+f7 to step through. Commented Dec 26, 2019 at 21:08

3 Answers 3

4

What you think it is happening

a, b, c = 1, 2, 3
b, a, c = c, b, a
print(a, b, c)
# 2, 3, 1
c, a, b = b, a, c
print(a, b, c)
# 2, 1, 3
a, b, c = c, a, b
print(a, b, c)
# 3, 2, 1

What is actually happening

a, b, c = 1, 2, 3
# a, b, c = c, a, b = b, a, c = c, b, a
temp = c, b, a
a, b, c = temp
print(a, b, c)
# 3 2 1
c, a, b = temp
print(a, b, c)
# 2 1 3
b, a, c = temp
print(a, b, c)
# 2 3 1

Basically: the loading happen from the right according to c, b, a, while the storing happen from left to right.

This is evidenced by disassembling the expression:

import dis


def chained_assign():
    a, b, c = 1, 2, 3
    a, b, c = c, a, b = b, a, c = c, b, a
    return a, b, c


dis.dis(chained_assign)

Output:

  5           0 LOAD_CONST               4 ((1, 2, 3))
              2 UNPACK_SEQUENCE          3
              4 STORE_FAST               0 (a)
              6 STORE_FAST               1 (b)
              8 STORE_FAST               2 (c)

  6          10 LOAD_FAST                2 (c)
             12 LOAD_FAST                1 (b)
             14 LOAD_FAST                0 (a)
             16 BUILD_TUPLE              3
             18 DUP_TOP
             20 UNPACK_SEQUENCE          3
             22 STORE_FAST               0 (a)
             24 STORE_FAST               1 (b)
             26 STORE_FAST               2 (c)
             28 DUP_TOP
             30 UNPACK_SEQUENCE          3
             32 STORE_FAST               2 (c)
             34 STORE_FAST               0 (a)
             36 STORE_FAST               1 (b)
             38 UNPACK_SEQUENCE          3
             40 STORE_FAST               1 (b)
             42 STORE_FAST               0 (a)
             44 STORE_FAST               2 (c)

  7          46 LOAD_FAST                0 (a)
             48 LOAD_FAST                1 (b)
             50 LOAD_FAST                2 (c)
             52 BUILD_TUPLE              3
             54 RETURN_VALUE

Notice the order of the STORE_FAST and LOAD_FAST instructions from line 6.

Additional discussion here:

Sign up to request clarification or add additional context in comments.

Comments

1

As norok indicated, storage and loading happen from different directions. If we take the following code we can see what python is doing under the hood.

import dis

def foo():
    a, b, c = 1, 2, 3
    a, b, c = c, a, b = b, a, c = c, b, a

dis.dis(foo)

Below you see the bytecode. To the right of the comments, you see the values for variables a, b, and c as well as the memory stack at the end of the operation. You'll see the DUP_TOP command negate the assignments at various steps so only the first load and last store appear to do anything. This would also explain why a, b, c = a, a, a = b, b, b = c, c, c = b, a, c = c, b, a still evaluates to (2, 3, 1).

                                                      # a b c stack

4           0 LOAD_CONST               4 ((1, 2, 3))  # - - - [(1, 2, 3)]
            3 UNPACK_SEQUENCE          3              # - - - [1, 2, 3]
            6 STORE_FAST               0 (a)          # 1 - - [2, 3]
            9 STORE_FAST               1 (b)          # 1 2 - [3]
           12 STORE_FAST               2 (c)          # 1 2 3 []

5          15 LOAD_FAST                2 (c)          # 1 2 3 [3]
           18 LOAD_FAST                1 (b)          # 1 2 3 [3, 2]
           21 LOAD_FAST                0 (a)          # 1 2 3 [3, 2, 1]
           24 BUILD_TUPLE              3              # 1 2 3 [(3, 2, 1)]
           27 DUP_TOP                                 # 1 2 3 [(3, 2, 1), (3, 2, 1)]
           28 UNPACK_SEQUENCE          3              # 1 2 3 [3, 2, 1, (3, 2, 1)]
           31 STORE_FAST               0 (a)          # 3 2 3 [2, 1, (3, 2, 1)]
           34 STORE_FAST               1 (b)          # 3 2 3 [1, (3, 2, 1)]
           37 STORE_FAST               2 (c)          # 3 2 1 [(3, 2, 1)]
           40 DUP_TOP                                 # 3 2 1 [(3, 2, 1), (3, 2, 1)]
           41 UNPACK_SEQUENCE          3              # 3 2 1 [3, 2, 1, (3, 2, 1)]
           44 STORE_FAST               2 (c)          # 3 2 3 [2, 1, (3, 2, 1)]
           47 STORE_FAST               0 (a)          # 2 2 3 [1, (3, 2, 1)]
           50 STORE_FAST               1 (b)          # 2 1 3 [(3, 2, 1)]
           53 UNPACK_SEQUENCE          3              # 2 1 3 [3, 2, 1]
           56 STORE_FAST               1 (b)          # 2 3 3 [2, 1]
           59 STORE_FAST               0 (a)          # 2 3 3 [1]
           62 STORE_FAST               2 (c)          # 2 3 1 []

3 Comments

a, b, c = a, a, a = b, b, b = c, c, c = b, a, c = c, b, a is still (2, 3, 1) and this logic does not explain it
Perhaps this helps ;-)
Yeah, I was just working through the bytecode and realized that DUP_TOP is what carried the values through. That was my first time digging through the bytecode like that, so thanks for the nudge.
1

It is because, when you write like this it means -: a = c = b = c value of a becomes c, the value of c becomes b and the value of b becomes c. so in last the change in the variable is taking place for b not a.

so the output would be-:

2 3 1

not-:

3 2 1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.