作为徘徊如何实际实施列表理解的副产品,我为您的问题找到了一个很好的答案。
在 Python 2 中,查看为简单列表理解生成的字节码:
>>> s = compile('[i for i in [1, 2, 3]]', '', 'exec')
>>> dis(s)
1 0 BUILD_LIST 0
3 LOAD_CONST 0 (1)
6 LOAD_CONST 1 (2)
9 LOAD_CONST 2 (3)
12 BUILD_LIST 3
15 GET_ITER
>> 16 FOR_ITER 12 (to 31)
19 STORE_NAME 0 (i)
22 LOAD_NAME 0 (i)
25 LIST_APPEND 2
28 JUMP_ABSOLUTE 16
>> 31 POP_TOP
32 LOAD_CONST 3 (None)
35 RETURN_VALUE
它本质上转化为一个简单for-loop
的 ,这就是它的语法糖。结果,与for-loops
apply 相同的语义:
a = []
for i in [1, 2, 3]
a.append(i)
print(i) # 3 leaky
在列表理解的情况下,(C)Python 使用“隐藏列表名称”和特殊指令LIST_APPEND
来处理创建,但实际上仅此而已。
因此,您的问题应该概括为为什么 Python 会写入for-loop
s 中的 for 循环变量;Eli Bendersky 的博客文章很好地回答了这个问题。
正如其他人所提到的那样,Python 3 已经改变了列表理解语义以更好地匹配生成器的语义(通过为理解创建单独的代码对象),并且本质上是以下内容的语法糖:
a = [i for i in [1, 2, 3]]
# equivalent to
def __f(it):
_ = []
for i in it
_.append(i)
return _
a = __f([1, 2, 3])
这不会泄漏,因为它不像 Python 2 等效项那样在最高范围内运行。i
被泄漏,仅在该__f
函数中作为局部变量被销毁。
如果您愿意,请查看通过运行为 Python 3 生成的字节码dis('a = [i for i in [1, 2, 3]]')
。您将看到如何加载“隐藏”代码对象,然后最后进行函数调用。