40
In [55]: a = 5

In [56]: b = 6

In [57]: (a, b) = (b, a)

In [58]: a
Out[58]: 6

In [59]: b
Out[59]: 5

How does this swapping of values of a and b work internally? Its definitely not using a temp variable.

martineau
  • 119,623
  • 25
  • 170
  • 301
praveen
  • 3,193
  • 2
  • 26
  • 30
  • 2
    It may interest you to view the disassembly of your code with [`dis`](http://docs.python.org/3/library/dis.html). Spoliers: the bytecode instruction [`ROT_TWO`](http://docs.python.org/2/library/dis.html#opcode-ROT_TWO) is used. – Kevin Jan 10 '14 at 15:05

1 Answers1

95

Python separates the right-hand side expression from the left-hand side assignment. First the right-hand side is evaluated, and the result is stored on the stack, and then the left-hand side names are assigned using opcodes that take values from the stack again.

For tuple assignments with 2 or 3 items, Python just uses the stack directly:

>>> import dis
>>> def foo(a, b):
...     a, b = b, a
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                1 (b)
              3 LOAD_FAST                0 (a)
              6 ROT_TWO             
              7 STORE_FAST               0 (a)
             10 STORE_FAST               1 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

After the two LOAD_FAST opcodes (which push a value from a variable onto the stack), the top of stack holds [a, b]. The ROT_TWO opcode swaps the top two positions on the stack so the stack now has [b, a] at the top. The two STORE_FAST opcodes then takes those two values and store them in the names on the left-hand side of the assignment. The first STORE_FAST pops a value of the top of the stack and puts it into a, the next pops again, storing the value in b. The rotation is needed because Python guarantees that assignments in a target list on the left-hand side are done from left to right.

For a 3-name assignment, ROT_THREE followed by ROT_TWO is executed to reverse the top three items on the stack.

For longer left-hand-side assignments, an explicit tuple is built:

>>> def bar(a, b, c, d):
...     d, c, b, a = a, b, c, d
... 
>>> dis.dis(bar)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 LOAD_FAST                2 (c)
              9 LOAD_FAST                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_FAST               3 (d)
             21 STORE_FAST               2 (c)
             24 STORE_FAST               1 (b)
             27 STORE_FAST               0 (a)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        

Here the stack with [d, c, b, a] is used to build a tuple (in reverse order, BUILD_TUPLE pops from the stack again, pushing the resulting tuple onto the stack), and then UNPACK_SEQUENCE pops the tuple from the stack again, pushes all elements back from the tuple back onto the stack again for the STORE_FAST operations.

The latter may seem like a wasteful operation, but the right-hand side of an assignment may be something entirely different, a function call that produces a tuple perhaps, so the Python interpreter makes no assumptions and uses the UNPACK_SEQUENCE opcode always. It does so even for the two and three-name assignment operations, but a later (peephole) optimization step replaces a BUILD_TUPLE / UNPACK_SEQUENCE combination with 2 or 3 arguments with the above ROT_TWO and ROT_THREE opcodes for efficiency.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 5
    +1 for **The rotation is needed because Python guarantees that assignments in a target list on the left-hand side are done from left to right.**. I did not know that. – treehouse May 13 '16 at 19:25
  • 1
    What's the ELI5 of this explanation? – JoeTheShmoe Aug 08 '21 at 22:01
  • Can we relate the two values stored on the stack as if they are two temporary variables? I know may not be technically correct, but just for understanding sake – Sreenikethan I Sep 20 '22 at 16:57
  • @JoeTheShmoe: if you are 5, I'd say: Python does some complicated juggling to get everything in the right place (and perhaps, if I could, juggle some balls to demonstrate. Juggling is just as complicated until you get the hang of it, and a 5-year-old would most likely be very impressed, whether I can juggle or not). – Martijn Pieters Oct 07 '22 at 11:22
  • 1
    @SreenikethanI: the stack is basically *all* temporary variables, so yes. The stack is like a scratch-pad for Python to do what it does. – Martijn Pieters Oct 07 '22 at 11:23
  • Excellent answer, thanks! For reference, here's the [C code for `ROT_TWO`](https://github.com/python/cpython/blob/v3.9.16/Python/ceval.c#L1510), at least in Python3.9. It's apparently been replaced by a more generic `SWAP` in newer versions. – Eric Duminil Mar 02 '23 at 06:57