and*_*sdr 5 python scope list-comprehension python-3.x
考虑以下:
def f():
a = 2
b = [a + i for i in range(3)]
f()
Run Code Online (Sandbox Code Playgroud)
这没有问题.据我了解(请纠正我,如果我错了),列表推导表达式引入了一个新的范围,但由于它是在一个函数内创建的(而不是一个类),它可以访问周围范围,包括变量a.
相反,如果我要进入调试模式,请在上面的第3行停止,然后在解释器中手动编写以下内容
>>> b = [a + i for i in range(3)]
Run Code Online (Sandbox Code Playgroud)
我收到一个错误:
Traceback (most recent call last):
File "<string>", line 293, in runcode
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 1, in <listcomp>
NameError: global name 'a' is not defined
Run Code Online (Sandbox Code Playgroud)
为什么是这样?当我在调试模式下停在给定的行时,是否可以访问与运行时相同的范围?
(顺便说一下,我正在使用PyScripter)
不,你的范围不一样.
Python 在编译时确定在什么范围内查找哪些变量.列表解析获得自己的范围,所以在list解析使用名称是本地到列表理解,关闭(外地),或全局.
在函数范围中,a是对列表理解的闭包; 编译器知道a位于父范围内f.但是,如果在交互式提示中输入相同的表达式,则没有嵌套的作用域,因为没有同时编译的周围函数.因此,a编译器假设它是全局的:
>>> import dis
>>> dis.dis(compile("b = [a + i for i in range(3)]", '<stdin>', 'single').co_consts[0])
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 16 (to 25)
9 STORE_FAST 1 (i)
12 LOAD_GLOBAL 0 (a)
15 LOAD_FAST 1 (i)
18 BINARY_ADD
19 LIST_APPEND 2
22 JUMP_ABSOLUTE 6
>> 25 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
一个LOAD_GLOBAL字节码用于a这里(该.0是range(3)为理解迭代器).
但是在功能范围内:
>>> def f():
... a = 2
... b = [a + i for i in range(3)]
...
>>> dis.dis(f.__code__.co_consts[2])
3 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 16 (to 25)
9 STORE_FAST 1 (i)
12 LOAD_DEREF 0 (a)
15 LOAD_FAST 1 (i)
18 BINARY_ADD
19 LIST_APPEND 2
22 JUMP_ABSOLUTE 6
>> 25 RETURN_VALUE
>>> f.__code__.co_cellvars
('a',)
Run Code Online (Sandbox Code Playgroud)
a加载LOAD_DEREF,加载第一个闭包(名为的单元格变量'a').
在交互式提示中测试类似列表理解时,您必须提供自己的嵌套范围; 将表达式包装在函数中:
>>> def f(a):
... return [a + i for i in range(3)]
...
>>> f(a)
[2, 3, 4]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
309 次 |
| 最近记录: |