列表理解:在调试模式下和正常运行时在作用域方面的不同行为 [英] List comprehensions: different behaviour with respect to scope in debug mode and in normal runtime

查看:44
本文介绍了列表理解:在调试模式下和正常运行时在作用域方面的不同行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下内容:

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

运行没有问题.据我了解(如果我错了,请纠正我),列表理解表达式引入了新的作用域,但是由于它是在函数(而不是类)中创建的,因此可以访问周围的范围,包括变量 a .

This runs without problems. As I understand it (please correct me if I'm wrong, though), the list comprehension expression introduces a new scope, but since it is created within a function (as opposed to, say, a class), it has access to the surrounding scope, including the variable a.

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

In contrast, if I were to enter debug mode, stop at line 3 above, and then just manually write the following in the interpreter

>>> b = [a + i for i in range(3)]

我得到一个错误:

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

这是为什么?当我在调试模式下停在给定的行时,我可以访问的范围是否与运行时的范围相同?

Why is this? When I'm stopped at a given line in debug mode, isn't the scope that I have access to the same as what it would be at runtime?

(顺便说一下,我正在使用PyScripter)

(I'm using PyScripter, by the way)

推荐答案

不,您获得的范围不完全相同.

No, you don't quite get the same scope.

Python在编译时确定 在什么范围内查找哪些变量.列表理解有其自身的范围,因此列表推导中使用的名称是列表推导中的本地 ,闭包(非本地)或全局变量.

Python determines at compile time what variables are looked up in what scope. List comprehensions get their own scope, so names used in the list comprehension are either local to the list comprehension, closures (nonlocal), or globals.

在功能范围内, a 是列表理解的闭包;编译器知道 a 位于 f 的父作用域中.但是,如果在交互式提示中输入相同的表达式,则不会有嵌套作用域,因为不会同时编译周围的函数.结果,编译器假定 a 为全局变量:

In the function scope, a is a closure to the list comprehension; the compiler knows a is located in the parent scope of f. But if you enter the same expression in an interactive prompt, there is no nested scope because there is no surrounding function being compiled at the same time. As a result, a is assumed by the compiler to be a global instead:

>>> 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

A LOAD_GLOBAL 字节码用于 a 此处(.0range(3) 可迭代的理解).

A LOAD_GLOBAL bytecode is used for a here (the .0 is the range(3) iterable for the comprehension).

但是在功能范围内:

>>> 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',)

a 加载了 LOAD_DEREF ,并加载了第一个闭包(名为'a'的单元格变量).

a is loaded with LOAD_DEREF, loading the first closure (the cell variable named 'a').

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

When testing a list comprehension like that in an interactive prompt, you'll have to provide your own nested scope; wrap the expression in a function:

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

这篇关于列表理解:在调试模式下和正常运行时在作用域方面的不同行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆