This code:
a = [1, 2, 3]
print(*a, a.pop(0))
Python 3.8 prints 2 3 1
(does the pop
before unpacking).
Python 3.9 prints 1 2 3 1
(does the pop
after unpacking).
What caused the change? I didn’t find it in the changelog.
Edit: Not just in function calls but also for example in a list display:
a = [1, 2, 3]
b = [*a, a.pop(0)]
print(b)
Prints [2, 3, 1]
vs [1, 2, 3, 1]
. And Expression lists says “The expressions are evaluated from left to right” (that’s the link to Python 3.8 documentation), so I’d expect the unpacking expression to happen first.
Advertisement
Answer
I suspect this may have been an accident, though I prefer the new behavior.
The new behavior is a consequence of a change to how the bytecode for *
arguments works. The change is in the changelog under Python 3.9.0 alpha 3:
bpo-39320: Replace four complex bytecodes for building sequences with three simpler ones.
The following four bytecodes have been removed:
- BUILD_LIST_UNPACK
- BUILD_TUPLE_UNPACK
- BUILD_SET_UNPACK
- BUILD_TUPLE_UNPACK_WITH_CALL
The following three bytecodes have been added:
- LIST_TO_TUPLE
- LIST_EXTEND
- SET_UPDATE
On Python 3.8, the bytecode for f(*a, a.pop())
looks like this:
1 0 LOAD_NAME 0 (f)
2 LOAD_NAME 1 (a)
4 LOAD_NAME 1 (a)
6 LOAD_METHOD 2 (pop)
8 CALL_METHOD 0
10 BUILD_TUPLE 1
12 BUILD_TUPLE_UNPACK_WITH_CALL 2
14 CALL_FUNCTION_EX 0
16 RETURN_VALUE
while on 3.9, it looks like this:
1 0 LOAD_NAME 0 (f)
2 BUILD_LIST 0
4 LOAD_NAME 1 (a)
6 LIST_EXTEND 1
8 LOAD_NAME 1 (a)
10 LOAD_METHOD 2 (pop)
12 CALL_METHOD 0
14 LIST_APPEND 1
16 LIST_TO_TUPLE
18 CALL_FUNCTION_EX 0
20 RETURN_VALUE
In the old bytecode, the code pushes a
and (a.pop(),)
onto the stack, then unpacks those two iterables into a tuple. In the new bytecode, the code pushes a list onto the stack, then does l.extend(a)
and l.append(a.pop())
, then calls tuple(l)
.
This change has the effect of shifting the unpacking of a
to before the pop
call, but this doesn’t seem to have been deliberate. Looking at bpo-39320, the intent was to simplify the bytecode instructions, not to change the behavior, and the bpo thread has no discussion of behavior changes.