为什么列表推导中没有定义一个类变量而另一个是?

时间:2014-03-27 15:30:27

标签: python scope list-comprehension python-internals python-3.4

我刚刚阅读了这个问题的答案: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是?

2 个答案:

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