Python swapping lists

16,339

Solution 1

Looks like Python internally swaps the items. Check this program

a, b = [1, 2], [2, 3]

def func():
    a, b = b, a

import dis
dis.dis(func)

Output

  4           0 LOAD_FAST                0 (b)
              3 LOAD_FAST                1 (a)
              6 ROT_TWO             
              7 STORE_FAST               1 (a)
             10 STORE_FAST               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

So, Python pushes references from b and a in the stack with LOAD_FAST. So, now the top most element is the reference pointed by a and the next one is the reference pointed by b. Then it uses ROT_TWO to swap the top two elements of the stack. So, now, the top most element is the reference pointed by b and the next one is the reference pointed by a and then assigns the top two elements of the stack to a and b respectively with STORE_FAST.

That's how sorting is happening in the assignment statement, when the number of items we deal with is lesser than 4.

If the number of items is greater than or equal to four, it builds a tuple and unpacks the values. Check this program

a, b, c, d = [1, 2], [2, 3], [4, 5], [5, 6]

def func():
    a, b, c, d  = d, c, b, a

import dis
dis.dis(func)

Output

  4           0 LOAD_FAST                0 (d)
              3 LOAD_FAST                1 (c)
              6 LOAD_FAST                2 (b)
              9 LOAD_FAST                3 (a)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_FAST               3 (a)
             21 STORE_FAST               2 (b)
             24 STORE_FAST               1 (c)
             27 STORE_FAST               0 (d)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE

Solution 2

Because Python assignment first evaluates the right-hand-side expression, then applies the result to the left-hand-side targets.

So, first, Python creates (<reference to list b>, <reference to list a>) as a tuple, then assigns the first item in that tuple to a, and the second item in that tuple to b. This swaps the references around neatly.

You could expand the assignment to read it like this:

tmp = b, a
a = tmp[0]
b = tmp[1]
Share:
16,339

Related videos on Youtube

Aswin Murugesh
Author by

Aswin Murugesh

Head of Engineering at Ongil Pvt Ltd contact me : facebook Twitter mail: [email protected]

Updated on October 20, 2022

Comments

  • Aswin Murugesh
    Aswin Murugesh over 1 year

    In python, when I assign a list to another, like:

    a = [1,2,3]
    b = a
    

    Now b and a point to the same list. Now considering two lists,

    a = [1,2,3]
    b = [4,5,6]
    a,b = b,a
    

    Now how is it that they are swapped like any other data type and does not end up both pointing to the same list?

  • thefourtheye
    thefourtheye over 10 years
    Looks like Python doesn't create a tuple here. Please check my answer.
  • Martijn Pieters
    Martijn Pieters over 10 years
    No, because the assignment is optimised.
  • Martijn Pieters
    Martijn Pieters over 10 years
    That comment went out a little too early (on my mobile). The assignment is optimised; the compiler skips creating a tuple when multiple assignment is used. There is no point. But for understanding what happens imagining it is a tuple is fine. In the expanded assignment example tmp is a tuple.
  • thefourtheye
    thefourtheye over 10 years
    Yup. That was my initial understanding. Even my first edit had that tuple based answer only. I simply tried dis and got surprised. :)
  • thefourtheye
    thefourtheye over 10 years
    @MartijnPieters That's why I had to make references in bold.
  • thefourtheye
    thefourtheye over 10 years
    @MartijnPieters Got you :) Please check my updated answer, including the tuple unpacking stuff.
  • Martijn Pieters
    Martijn Pieters over 10 years
    As for the optimisation I talked about; it is indeed limited to two and three item unpacks; see peephole.c if you are interested. For two items, BUILD_TUPLE and UNPACK_SEQUENCE is replaced by ROT_TWO, for three items it is replaced by ROT_THREE then ROT_TWO. Because 4 items would require three opcodes (ROT_FOUR, then ROT_THREE then ROT_TWO) it is no longer worth optimizing for.
  • thefourtheye
    thefourtheye over 10 years
    @MartijnPieters Good that you read this. :) Otherwise this answer would have been misleading. Updated that sentence. Please check now
  • Steve Jessop
    Steve Jessop over 10 years
    I think it's pretty much optional whether you think of the LHS of the assignment as being a tuple, or as being a grammar production consisting of a comma-separated list of entities (which themselves may be such lists). Of course it's both, but in general when a comma-separated list of sub-expressions is evaluated for its result a tuple is created. When that same grammar production (called testlist in the formal Python grammar) is assigned to, one isn't.
  • Martijn Pieters
    Martijn Pieters over 10 years
    @SteveJessop: the compiler produces a BUILD_TUPLE opcode in both cases, but the peephole optimizer then replaces that opcode with ROT_ opcodes when there is an assignment on the LHS and only 3 or 2 values are involved. For more elements, tuples are still actually created.
  • Steve Jessop
    Steve Jessop over 10 years
    @MartijnPieters: ah, thanks. I didn't realise this was a special case. @Aswin: I think it's also instructive to work through why a[:], b[:] = b, a doesn't swap the contents of the lists -- once it's obvious to you why not, and also obvious why a, b = b, a does swap the references, then you probably understand how the unpacking and assignment works :-)