Can someone say what’s the difference of these two approaches?
Using ( ) a, (b, c) = "a", "bc"
Using [ ] a, [b, c] = "a", "bc"
Both the approaches are unpacking it in the same way. And random people in the internet say the first approach of using “( )” is faster in time because of tuple unpacking.
And personally I find using “[ ]” on the left hand side of the “=” looks so weird and not pythonic way of unpacking.
So is there any special difference of these two unpacking?
Python 3.12.6 (main, Sep 10 2024, 00:05:17) [GCC 9.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.21.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: %timeit a, [b, c] = "a", "bc"
36.4 ns ± 0.533 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [2]: %timeit a, (b, c) = "a", "bc"
36.2 ns ± 0.112 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
It sounds to me like somewhere along the telephone chain there was a misgeneralization from “tuples are faster than lists"[1] to “sequence unpacking with parentheses is faster than sequence unpacking with brackets.” That isn’t the case. Both a, (b, c) = ... and a, [b, c] = ... are equivalent syntaxes for sequence unpacking that are compiled to the same bytecode.
What the original writer of that claim may have been thinking about is the fact that tuple construction is often faster than list construction, for a variety of reasons. This is actually a somewhat complicated bit of trivia, the details of which change a lot between CPython versions. But in short creating tuples is often faster than creating lists because
more compile-time optimizations apply to tuple construction, especially in older versions of CPython.
constructing a new tuple takes less work at runtime than constructing a new list.
An implication of the above is that it is true that this:
a, b = (b, a)
is faster than this:
a, b = [b, a]
In the first case CPython optimizes away the tuple creation entirely and emits bytecode to directly swap a and b, while in the second case it resorts to building and unpacking an intermediate list[2].
for some meanings of “faster”, in some circumstances ↩︎
This somewhat surprises me. Given that the code generator needs to verify that the number of elements in (b, a) equals the number of elements in a, b to generate the SWAP instruction, it doesn’t seem like it would take that much more work to identify [b, a] as an anonymous list that won’t be used beyond the unpacking operation. The parse tree would immediately distinguish a regular list display from a list comprehension, so it seem like a, b = [b, a] could easily be optimized just like a, b = (b, a).
I think this is just because it was manually optimized, not something inherent to tuples vs lists. I think the tuple version is much more common and so it was a target for optimization.
The list version could be optimized too but I a) doubt it would apply very often and b) a higher-level strategy (e.g. improving the JIT) is probably better ROI.