为什么在平面理解的第二个变量上会出现 UnboundLocalError? [英] Why does the UnboundLocalError occur on the second variable of the flat comprehension?

查看:26
本文介绍了为什么在平面理解的第二个变量上会出现 UnboundLocalError?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里回答了一个问题:python2 中的理解列表工作正常,但在 python3 中出现错误

I answered a question here: comprehension list in python2 works fine but i get an error in python3

OP 的错误是对最大范围和索引使用相同的变量:

OP's error was using the same variables for max range and indices:

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 ]

这只是一个 Python-3 错误,与添加到理解中的作用域有关,以避免此处定义的变量泄漏".更改变量名称可以解决这个问题.

This is a Python-3 error only, and related to the scopes that were added to the comprehension to avoid the variables defined here "leaking". Changing the variable names fixes that.

错误是:

UnboundLocalError: local variable 'y' referenced before assignment

因为外部的、全局的 y 被局部作用域遮蔽了.

because outer, global y is shadowed by the local scope.

我的问题是:为什么我在 y 而不是在 zx 上得到错误?

My question is: why do I get the error on y and not on z or x ?

如果我删除 x 上的循环,错误将移至 z:

If I remove the loop on x, the error moves to 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 函数在所有其他表达式之前被评估,这使得 x 的值保持不变.但具体原因仍有待寻找.使用 Python 3.4.3.

it works. So I'm suspecting that the first range function is evaluated before all the other expressions, which leaves the value of x intact. But the exact reason is still to be found. Using Python 3.4.3.

推荐答案

此行为在 参考文档(重点是我的).

This behaviour is (implicitly) described in the reference documentation (emphasis mine).

然而,除了最左边的 for 子句中的可迭代表达式之外,推导式在单独的隐式嵌套范围内执行.这确保分配给目标列表中的名称不会泄漏"到封闭范围内.

However, aside from the iterable expression in the leftmost for clause, the comprehension is executed in a separate implicitly nested scope. This ensures that names assigned to in the target list don’t "leak" into the enclosing scope.

最左边的 for 子句中的可迭代表达式在封闭范围内直接计算,然后作为参数传递给隐式 [sic] 嵌套范围. 后续 for 子句和最左边的 for 子句中的任何过滤条件都不能在封闭范围内进行评估,因为它们可能取决于从最左边的可迭代对象中获得的值.例如:[x*y for x in range(10) for y in range(x, x+10)].

The iterable expression in the leftmost for clause is evaluated directly in the enclosing scope and then passed as an argument to the implictly [sic] nested scope. Subsequent for clauses and any filter condition in the leftmost for clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: [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 发生的原因.

As the name x in for the leftmost iterable is read in the enclosing scope as opposed to the nested scope then there is no name conflict between these two uses of x. The same is not true for y, which is why it is where the UnboundLocalError occurs.

至于为什么会发生这种情况:列表推导或多或少是 list() 的语法糖,因此它将使用与生成器相同的代码路径表达(或至少以相同的方式行事).生成器表达式对最左边的 for 子句中的可迭代表达式求值,以便在生成器表达式更清晰时进行错误处理.考虑以下代码:

As to why this happens: a list comprehension is more-or-less syntactic sugar for list(<generator expression>), so it's going to be using the same code path as a generator expression (or at least behave in the same way). Generator expressions evaluate the iterable expression in the leftmost for clause to make error handling when the generator expression somewhat saner. Consider the following code:

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) 类型错误在第 2 行而不是第 3 行出现.因此,更容易诊断问题发生的位置和原因.如果它发生在第 3 行,那么您可能会错误地认为是 x + 1 语句导致了错误.

y is clearly the wrong type and so the addition will raise a TypeError. By evaluating range(y + 1) immediately that type error is raised on line 2 rather than line 3. Thus, it is easier to diagnose where and why the problem occurred. Had it occurred on line 3 then you might mistakenly assume that it was the x + 1 statement that caused the error.

有一个错误报告 此处 提到了这种行为.它被解决为不是错误"因为希望列表推导式和生成器表达式具有相同的行为.

There is a bug report here that mentions this behaviour. It was resolved as "not a bug" for reason that it is desirable that list comprehensions and generator expressions have the same behaviour.

这篇关于为什么在平面理解的第二个变量上会出现 UnboundLocalError?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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