Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
141 views
in Technique[技术] by (71.8m points)

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

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...