不直观的UnboundLocalError行为的原因

时间:2009-07-27 15:46:35

标签: python

注意:问题here非常相似。但是,请耐心等待;我的问题不是“为什么会发生错误”,而是“为什么Python实现为在这种情况下抛出错误。”

我偶然发现了这个:

a = 5
def x()
    print a
    a = 6
x()

抛出UnboundLocalException。现在,我确实知道为什么会发生这种情况(在此范围的后面,a被绑定,因此a在整个范围内被认为是本地的。)

在这种情况下:

a = 5
def x()
    print b
    b = 6
x()

这非常有意义。但第一种情况有一个直观的逻辑,这意味着:

a = 5
def x()
    print globals()["a"]
    a = 6 # local assignment
x()

我想有一个原因,为什么不允许“直觉”版本,但它是什么?虽然这可能是“明确比隐含更好”的情况,但摆弄globals()对我来说总觉得有点不洁。

为了正确看待这一点,发生在我身上的实际情况是别人的剧本,我不得不改变一下。在我的(短暂的)更改中,我在脚本运行时进行了一些文件重命名,因此我插入了

import os
os.rename("foo", "bar")

进入脚本。这个插入发生在一个函数内部。模块已经在顶层导入os(我没有检查过),并且在函数内部进行了一些os.somefunction调用,但是在插入之前。这些来电显然会触发UnboundLocalException

那么,有人可以向我解释这个实现背后的原因吗?是为了防止用户犯错误? “直观”的方式会让字节码编译器的内容变得更复杂吗?或者是否存在我没​​有想到的歧义?

3 个答案:

答案 0 :(得分:5)

具有相同的,相同的名称指的是在相同的线性代码流中完全不同的变量是如此令人难以置信的复杂性,以至于它错开了思想。考虑:

def aaaargh(alist):
  for x in alist:
    print a
    a = 23

这个代码应该在你想要的Python变体中做什么?同一个a语句中的print是指循环第一段与第二段完全不同且不相关的变量(假设有第二段)?即使对于一个项目的列表,它的工作方式是否与非循环代码不同?说真的,疯狂就是这样 - 甚至不考虑可怕的实施问题,只是试图记录和教导这可能会让我转换语言。

对于语言,其实施者,教师,学习者,实践者来说,承担所有这些概念负担的基础是什么?支持和鼓励半隐藏,非显性使用GLOBAL VARIABLES ?!这似乎不值得一个目标,现在呢?!

答案 1 :(得分:1)

我认为可能存在歧义。

a = 5
def x():
    print a
    a = 6  # could be local or trying to update the global variable
x()

可能如你所愿:

a = 5
def x():
    print globals()["a"]
    a = 6 # local assignment
x()

或者可能是他们想要将全局变量更新为6:

a = 5
def x():
    global a
    print a
    a = 6
x()

答案 2 :(得分:0)

这是范围界定的基本副作用。 python开发人员认为全局变量不应该在您尝试使用它的范围内。例如,以此为例:

a = 5
def x():
    a = 6
    print a
x()
print a

这会输出6 5

无论如何都有generally considered bad practice全局变量,因此python开发人员限制了这一点。您必须显式地使全局变量可访问才能访问它。这实际上是为了防止歧义。考虑一下:

a = 5
def x():
    a = 6
    print a
    y()
def y():
    global a
    a = a + 1
    print a
x()
print a

如果x()认为a是本地的,并完成了分配,则会输出6 6 7。编写x()的人可能没有考虑过y()会使用名为a的全局变量。因此导致y()异常行为。幸运的是,python scopping使得x()的开发人员不必担心y()的开发人员如何实现y(),只是它实现了它应该做的事情。结果,这会输出6 6 6(数字),就像它应该的那样。

因此,UnboundLocalException非常直观。