简体   繁体   中英

How does swapping of members in tuples (a,b)=(b,a) work internally?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM