我在这里回答了一个问题:comprehension list in python2 works fine but i get an error in python3
OP的错误是对最大范围和索引使用了相同的变量:
npm
这仅是Python-3错误,与为避免在此定义的变量“泄漏”而添加到理解中的范围有关。更改变量名称可以解决此问题。
错误是:
x = 12
y = 10
z = 12
n = 100
ret_list = [ (x,y,z) for x in range(x+1) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]
因为外部全局UnboundLocalError: local variable 'y' referenced before assignment
被本地作用域遮蔽了。
我的问题是:为什么我在y
而不是y
或z
上看到错误?
编辑:如果我删除x
上的循环,则错误移至x
:
z
如果我只做一个循环:
>> ret_list = [ (x,y,z) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]
UnboundLocalError: local variable 'z' referenced before assignment
有效。因此,我怀疑第一个ret_list = [ (x,y,z) for y in range(y+1) if x+y+z!=n ]
函数在所有其他表达式之前 求值,而range
的值保持不变。但是确切的原因尚待发现。使用Python 3.4.3。
答案 0 :(得分:6)
reference documentation(重点是我的)中(隐式)描述了此行为。
但是,除了最左边的
for
子句中的可迭代表达式之外,该理解是在单独的隐式嵌套范围中执行的。这样可以确保在目标列表中分配的名称不会“泄漏”到封闭范围内。最左边的
for
子句中的可迭代表达式在封闭范围内直接求值,然后作为参数传递给隐式[sic]嵌套范围。随后的for
子句以及最左边的for
子句中的任何过滤条件都不能在封闭范围内进行评估,因为它们可能取决于从最左边的iterable获得的值。例如:[x*y for x in range(10) for y in range(x, x+10)]
。
这意味着:
list_ = [(x, y) for x in range(x) for y in range(y)]
等同于:
def f(iter_):
for x in iter_:
for y in range(y):
yield x, y
list_ = list(f(iter(range(x))))
由于最左边的可迭代名称x
是在封闭范围内而不是嵌套范围内读取的,因此x
的这两种用法之间没有名称冲突。 y
并非如此,这就是UnboundLocalError
发生的原因。
为什么会这样:列表理解是list(<generator expression>)
的语法糖,所以它将使用与生成器表达式相同的代码路径(或至少以相同的方式运行) )。生成器表达式在最左边的for
子句中评估可迭代表达式,以在生成器表达式更精巧时进行错误处理。考虑以下代码:
y = None # line 1
gen = (x + 1 for x in range(y + 1)) # line 2
item = next(gen) # line 3
y
显然是错误的类型,因此加法将引发TypeError
。通过立即评估range(y + 1)
,第二行而不是第三行会引发类型错误。因此,更容易诊断问题发生的位置和原因。如果它发生在第3行,那么您可能会错误地认为是导致错误的x + 1
语句。
有一个错误报告here提到了此行为。由于希望列表理解和生成器表达式具有相同的行为,因此将其解析为“不是bug”。