为什么Python 2中的生成器表达式和dict/set理解使用嵌套函数而不是列表理解?
列表理解的代码直接放在使用它们的函数中,如下所示:为什么Python 2中的生成器表达式和dict/set理解使用嵌套函数而不是列表理解?,python,bytecode,python-internals,Python,Bytecode,Python Internals,列表理解的代码直接放在使用它们的函数中,如下所示: >>> dis.dis((lambda: [a for b in c])) 1 0 BUILD_LIST 0 3 LOAD_GLOBAL 0 (c) 6 GET_ITER >> 7 FOR_ITER 12 (
>>> dis.dis((lambda: [a for b in c]))
1 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (c)
6 GET_ITER
>> 7 FOR_ITER 12 (to 22)
10 STORE_FAST 0 (b)
13 LOAD_GLOBAL 1 (a)
16 LIST_APPEND 2
19 JUMP_ABSOLUTE 7
>> 22 RETURN_VALUE
>>> dis.dis((lambda: {a for b in c}))
1 0 LOAD_CONST 1 (<code object <setcomp> at 0x7ff41a3d59b0, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (c)
9 GET_ITER
10 CALL_FUNCTION 1
13 RETURN_VALUE
>>> dis.dis((lambda: {a for b in c}).func_code.co_consts[1])
1 0 BUILD_SET 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (b)
12 LOAD_GLOBAL 0 (a)
15 SET_ADD 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
而生成器表达式和dict/set理解大多放在单独的嵌套函数中,如下所示:
>>> dis.dis((lambda: [a for b in c]))
1 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (c)
6 GET_ITER
>> 7 FOR_ITER 12 (to 22)
10 STORE_FAST 0 (b)
13 LOAD_GLOBAL 1 (a)
16 LIST_APPEND 2
19 JUMP_ABSOLUTE 7
>> 22 RETURN_VALUE
>>> dis.dis((lambda: {a for b in c}))
1 0 LOAD_CONST 1 (<code object <setcomp> at 0x7ff41a3d59b0, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (c)
9 GET_ITER
10 CALL_FUNCTION 1
13 RETURN_VALUE
>>> dis.dis((lambda: {a for b in c}).func_code.co_consts[1])
1 0 BUILD_SET 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (b)
12 LOAD_GLOBAL 0 (a)
15 SET_ADD 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
dis.dis((lambda:{a代表c}中的b))
1 0加载常数1(<0x7ff41a3d59b0处的代码对象setcomp,文件“stdin”,第1行>)
3生成函数0
6加载_全局0(c)
9得到它
10调用函数1
13返回值
>>>dis.dis((lambda:{a代表c}.func_code.co_consts[1]))
1 0生成集0
3加载速度为0(.0)
>>国际热核实验堆12(至21)6
9商店1(b)
12加载_全局0(a)
15套加2套
18绝对跳跃6
>>21返回值
在Python3中,所有这些都放在一个嵌套函数中
为什么要将代码放在单独的嵌套函数中?我模模糊糊地记得很久以前读到过一些关于人们想要修正理解和/或genexpr变量溢出到周围范围的文章,这是对那个的修正还是什么
为什么列表理解的实现与Python 2中的其他理解不同?因为向后兼容?(我想我在介绍生成器表达式后听到了很多关于溢出修复的讨论,但我可能只是在阅读一些非常古老的讨论或其他内容)是的,你是对的。在Python3.x中,引入此选项是为了修复变量泄漏。引用了《华盛顿邮报》的一段话,据说是BDFL自己写的 我们还在Python3中做了另一个更改,以改进列表理解和生成器表达式之间的等价性在Python 2中,列表理解将循环控制变量“泄漏”到周围的范围中:
x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'
这是列表理解最初实现的产物;这是Python多年来“肮脏的小秘密”之一一开始,它是一种有意的妥协,让人们盲目地快速理解列表,虽然这对初学者来说不是一个常见的陷阱,但它肯定会偶尔刺痛人们。对于生成器表达式,我们无法执行此操作。生成器表达式使用生成器实现,生成器的执行需要单独的执行框架。因此,生成器表达式(特别是当它们在短序列上迭代时)的效率低于列表理解
然而,在Python3中,我们决定使用与生成器表达式相同的实现策略来修复列表理解的“肮脏小秘密”。因此,在Python3中,上面的示例(修改为使用print(x)
:-)将打印“before”,证明列表中的“x”暂时隐藏,但不会覆盖周围范围中的“x”
突出显示的文本回答了您的所有问题。以下是Nick Coghlan和Georg Brandl讨论以这种方式实施这些问题的基本原理的线索