列表理解:在调试模式和正常运行时,作用域的不同行为

2024-04-25 13:32:10 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑以下因素:

def f():
    a = 2
    b = [a + i for i in range(3)]
f()

运行起来没有问题。据我所知(如果我错了,请纠正我),list comprehension表达式引入了一个新的作用域,但是由于它是在函数(而不是类)中创建的,它可以访问周围的范围,包括变量a。在

相反,如果要进入调试模式,请在上面的第3行停止,然后在解释器中手动编写以下内容

^{pr2}$

我得到一个错误:

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

为什么会这样?当我在调试模式下停在给定的行时,我可以访问的范围与运行时的作用域不一样吗?在

(顺便说一下,我使用的是PyScripter)


Tags: 函数inforinput表达式deflinerange
2条回答

你在某个地方有运行代码,在另一个地方有源代码(你的编辑器)。当您在断点处停止代码时,它并不是真正的位置,而是在编辑器中执行之前查看当前代码行。因此,如果您在控制台中执行一些代码,它就有自己的作用域。作为证明,你可以在解释器中犯一些错误,但它不会影响代码的执行,没有任何例外,没有一个代码崩溃。在

不,你没有得到同样的范围。在

Python在编译时确定在哪个范围内查找哪些变量。List comprehensions get their own scope,因此在列表理解中使用的名称要么是列表理解的本地名称,要么是闭包(非本地的),要么是全局的。在

在函数作用域中,a是列表理解的闭包;编译器知道a位于{}的父作用域中。但是,如果在交互提示中输入相同的表达式,则不存在嵌套范围,因为没有同时编译的周围函数。因此,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

在这里,LOAD_GLOBAL字节码用于a(对于理解,.0是{}的可理解文件)。在

但是,在函数作用域中:

^{pr2}$

aLOAD_DEREF一起加载,加载第一个闭包(单元格变量'a')。在

在交互式提示中测试这样的列表理解时,您必须提供自己的嵌套范围;将表达式包装在函数中:

>>> def f(a):
...     return [a + i for i in range(3)]
...
>>> f(a)
[2, 3, 4]

相关问题 更多 >