Jea*_*bre 8 python scope list-comprehension python-3.x
我在这里回答了一个问题:python2中的理解列表工作正常,但我在python3中遇到错误
OP的错误是对最大范围和索引使用相同的变量:
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 ]
Run Code Online (Sandbox Code Playgroud)
这只是一个Python-3错误,与添加到理解中的范围有关,以避免此处定义的变量"泄漏".更改变量名称可修复该问题.
错误是:
UnboundLocalError: local variable 'y' referenced before assignment
Run Code Online (Sandbox Code Playgroud)
因为外部,全局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
Run Code Online (Sandbox Code Playgroud)
如果我只做一个循环:
ret_list = [ (x,y,z) for y in range(y+1) if x+y+z!=n ]
Run Code Online (Sandbox Code Playgroud)
有用.所以我怀疑在所有其他表达式之前range评估第一个函数,这样就保留了完整的值.但确切的原因仍有待找到.使用Python 3.4.3.x
这种行为(隐含地)在参考文档(强调我的)中描述.
但是,除了最左边的
for子句中的iterable表达式之外,理解还在一个单独的隐式嵌套作用域中执行.这可确保分配给目标列表中的名称不会"泄漏"到封闭范围中.最左边
for子句中的iterable表达式直接在封闭范围内计算,然后作为参数传递给implictly [sic]嵌套范围.最后一个for子句中的后续子句和任何过滤条件for无法在封闭范围内进行求值,因为它们可能取决于从最左边的可迭代获得的值.例如:[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)]
Run Code Online (Sandbox Code Playgroud)
相当于:
def f(iter_):
for x in iter_:
for y in range(y):
yield x, y
list_ = list(f(iter(range(x))))
Run Code Online (Sandbox Code Playgroud)
由于x最左边的iterable 的名称是在封闭范围内读取而不是嵌套范围,因此这两个用法之间没有名称冲突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
Run Code Online (Sandbox Code Playgroud)
y显然是错误的类型,所以增加将提出一个TypeError.通过range(y + 1)立即评估在第2行而不是第3行引发类型错误.因此,更容易诊断问题发生的位置和原因.如果它发生在第3行,那么您可能会错误地认为这是x + 1导致错误的语句.
有一个bug报告这里是提到此行为.由于列表推导和生成器表达式具有相同的行为,因此它被解析为"不是错误".