这段代码:
a = [1, 2, 3]
print(*a, a.pop(0))
Run Code Online (Sandbox Code Playgroud)
Python 3.8 打印2 3 1(在解包之前进行pop)。
Python 3.9 打印1 2 3 1(解压后执行pop)。
是什么导致了这种变化?我在变更日志中没有找到它。
编辑:不仅在函数调用中,而且在列表显示中:
a = [1, 2, 3]
b = [*a, a.pop(0)]
print(b)
Run Code Online (Sandbox Code Playgroud)
打印[2, 3, 1]vs[1, 2, 3, 1]打印 表达式列表显示“表达式从左到右求值”(这是 Python 3.8 文档的链接),所以我希望首先发生解包表达式。
use*_*ica 53
我怀疑这可能是一次意外,尽管我更喜欢新的行为。
新行为是参数字节码工作方式更改的结果*。更改位于Python 3.9.0 alpha 3 下的更改日志中:
bpo-39320:用三个更简单的字节码替换用于构建序列的四个复杂字节码。
以下四个字节码已被删除:
- BUILD_LIST_UNPACK
- BUILD_TUPLE_UNPACK
- 构建_设置_解压
- BUILD_TUPLE_UNPACK_WITH_CALL
添加了以下三个字节码:
- 列表到元组
- 列表_EXTEND
- 设置更新
在 Python 3.8 上, 的字节码f(*a, a.pop())如下所示:
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
Run Code Online (Sandbox Code Playgroud)
而在 3.9 上,它看起来像这样:
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
Run Code Online (Sandbox Code Playgroud)
在旧的字节码中,代码将a和压(a.pop(),)入堆栈,然后将这两个可迭代对象解压到一个元组中。在新的字节码中,代码将列表压入堆栈,然后执行l.extend(a)和l.append(a.pop()),然后调用tuple(l)。
此更改的效果是将解包转移a到调用之前pop,但这似乎不是故意的。查看bpo-39320,其目的是简化字节码指令,而不是更改行为,并且 bpo 线程没有讨论行为更改。