Why Python’s a, b = b, a Isn’t Magic – The Real Mechanics Explained
This article demystifies Python’s a, b = b, a idiom by detailing how tuple packing and unpacking work, the exact evaluation order, the CPython bytecode implementation, comparisons with C, C++, Java, JavaScript, and the performance and memory implications of the technique.
In Python the statement a, b = b, a is a common idiom for swapping two variables, but beginners often think it is “magic”. It is simply tuple packing and unpacking defined by the language reference: the right‑hand side is evaluated first as a tuple, then its elements are assigned to the left‑hand side targets from left to right.
This Is Just Ordinary Tuple Packing and Unpacking
The expression list b, a is treated as the tuple (b, a) . Python evaluates the tuple, then assigns its first element to a and the second to b . This eliminates the risk of one variable overwriting the other during assignment.
Conceptually the swap can be written as:
<code>tmp = (b, a)
a, b = tmp</code>Note that this is an assignment statement, not an expression, so it returns None . The evaluation order is fixed: Python first evaluates b then a , builds the tuple, and then assigns the values left‑to‑right.
Tuple Packing and Unpacking in Python
At a high level, a, b = b, a works because Python can pack multiple values into a tuple and unpack them. The right‑hand side creates a tuple (b, a) ; the left‑hand side a, b unpacks it, assigning the first element to a and the second to b . The same mechanism applies to any number of variables, e.g. x, y = y, x .
If the right‑hand side contains a function call, the function is evaluated before any assignment, guaranteeing that the old values are used.
CPython Implementation: Bytecode and Stack Operations
CPython translates a, b = b, a into stack‑based bytecode. Disassembling a simple function shows the sequence:
<code>>> import dis
>>> def swap(a, b):
... a, b = b, a
>>> dis.dis(swap)
2 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</code>The steps are:
LOAD_FAST pushes b onto the stack.
LOAD_FAST pushes a , so the stack holds [b, a] .
ROT_TWO swaps the top two stack items, yielding [a, b] .
STORE_FAST stores the top value into a , then the next into b .
The crucial opcodes are LOAD_FAST , ROT_TWO and STORE_FAST . No new tuple object is created for two‑element swaps because the peephole optimizer replaces the generic tuple construction with these stack rotations.
For three‑element swaps CPython uses ROT_THREE and ROT_TWO . For four or more elements it falls back to building a temporary tuple with BUILD_TUPLE and then unpacking it with UNPACK_SEQUENCE .
Variable Swaps in Other Languages
C (using a temporary variable)
<code>int temp = a;
a = b;
b = temp;</code>C requires an explicit temporary storage; the compiler emits a few machine instructions.
C++ (std::swap)
<code>T temp = std::move(a);
a = std::move(b);
b = std::move(temp);</code>Since C++11 std::swap typically uses move semantics, which may reduce copying for complex types.
Java (pass‑by‑value)
<code>void swap(Object x, Object y) {
Object temp = x;
x = y;
y = temp;
}</code>Because Java passes arguments by value, this method does not affect the caller’s variables; a temporary container is needed to swap actual values.
JavaScript (ES6 destructuring)
<code>let a = 1, b = 3;
[a, b] = [b, a];
console.log(a, b); // 3 1</code>Destructuring creates a temporary array [b, a] and then unpacks it, which incurs allocation and garbage‑collection overhead.
Rough Performance and Memory Comparison
Python’s swap involves two LOAD_FAST and two STORE_FAST operations plus a ROT_TWO . Interpreted bytecode and reference‑count handling make it slower than compiled C/C++ swaps, which may be 4–5× faster in micro‑benchmarks. JavaScript’s destructuring also allocates a temporary array, adding overhead.
Memory‑wise, CPython does not allocate a tuple for two‑ or three‑element swaps thanks to the peephole optimizer; for four or more elements it does allocate a temporary tuple. C/C++ swaps operate entirely in registers/stack without heap allocation.
Parsing and AST Perspective
Parsing a, b = b, a yields an Assign node whose targets is a Tuple of a and b , and whose value is a Tuple of b and a . No swapping occurs at parse time; the AST merely represents the structure, and the actual exchange happens during bytecode execution.
Intuitive Understanding and Key Takeaways
The statement is simply syntactic sugar for multiple assignment: evaluate the right‑hand side tuple first, then unpack it left‑to‑right. CPython implements the two‑element case with efficient stack operations ( LOAD_FAST , ROT_TWO , STORE_FAST ) without creating a temporary tuple. For more elements, a generic tuple build/unpack is used. Understanding this mechanism helps avoid confusion and shows how Python balances readability with reasonable performance.
Key points: Python’s swap is tuple unpacking, the evaluation order is fixed, the peephole optimizer replaces generic tuple construction with ROT_TWO / ROT_THREE for small numbers of variables, and while convenient, it is still slower than low‑level swaps in compiled languages.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.