我刚刚阅读了这个问题的答案:Accessing class variables from a list comprehension in the class definition
这有助于我理解为什么以下代码会产生NameError: name 'x' is not defined
:
class A:
x = 1
data = [0, 1, 2, 3]
new_data = [i + x for i in data]
print(new_data)
NameError
发生是因为x
未在列表推导的特殊范围中定义。但我无法理解为什么下面的代码没有任何错误。
class A:
x = 1
data = [0, 1, 2, 3]
new_data = [i for i in data]
print(new_data)
我得到输出[0, 1, 2, 3]
。但是我期待这个错误:NameError: name 'data' is not defined
因为我期待的就像前面的例子一样,名称x
没有在列表理解的范围中定义,同样,名称{{1}在列表理解的范围内也不会定义。
您能否帮我理解为什么data
未在列表理解的范围中定义,但x
是?
答案 0 :(得分:17)
data
是列表推导的源;它是传递给创建的嵌套作用域的一个参数。
列表解析中的所有内容都在一个单独的作用域中运行(基本上作为一个函数),但用于最左侧for
循环的可迭代除外。您可以在字节代码中看到这一点:
>>> def foo():
... return [i for i in data]
...
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0x105390390, file "<stdin>", line 2>)
3 LOAD_CONST 2 ('foo.<locals>.<listcomp>')
6 MAKE_FUNCTION 0
9 LOAD_GLOBAL 0 (data)
12 GET_ITER
13 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
16 RETURN_VALUE
<listcomp>
代码对象被称为函数,iter(data)
作为参数传递(CALL_FUNCTION
执行1位置参数,GET_ITER
结果)
<listcomp>
代码对象查找该参数:
>>> dis.dis(foo.__code__.co_consts[1])
2 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (i)
12 LOAD_FAST 1 (i)
15 LIST_APPEND 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
LOAD_FAST
调用是指传入的第一个也是唯一的位置参数;它在这里没有命名,因为从来没有一个函数定义给它命名。
列表推导中使用的任何其他名称(或者set或dict comprehension,或者生成器表达式)都是locals,closures或globals,而不是参数。
答案 1 :(得分:-2)
dis.dis
的答案很有趣,但实际上并没有解释为什么会发生这种情况。这是来自similar error:
如果名称绑定操作发生在代码块内的任何位置,则该块内对该名称的所有使用都被视为对当前块的引用。在绑定之前在块中使用名称时,这可能会导致错误。这个规则很微妙。 Python 缺少声明并允许名称绑定操作发生在代码块内的任何位置。代码块的局部变量可以通过扫描块的整个文本进行名称绑定操作来确定。
所以简单来说:data
不能引用 x
,因为块不受那个点的约束。无法引用 x
:既不能单独引用 x
,也不能引用 A.x
。
来源:python
docs。