非直观的UnboundLocalError行为原因
注意:这里有一个非常相似的问题 在这里。不过请耐心听我说;我的问题不是“为什么会出现这个错误”,而是“为什么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 个回答
这其实是作用域的一个基本副作用。Python的开发者决定全局变量在你尝试使用的作用域中是不可用的。举个例子:
a = 5
def x():
a = 6
print a
x()
print a
这个例子输出的是 6 5
。
总的来说,使用全局变量被认为是一个不好的做法,所以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的作用域规则让 x()
的开发者不需要担心 y()
的开发者是怎么实现 y()
的,只需要确保它能正常工作。因此,这个例子输出的是 6 6 6
(如预期那样)。
因此,UnboundLocalException
是非常直观的。
我觉得这里可能有点模糊。
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()
在同一段线性代码中,使用完全相同的名字来指代完全不同的变量,这种复杂性真是让人头疼。想想看:
def aaaargh(alist):
for x in alist:
print a
a = 23
你希望这段代码在你想要的Python版本中做什么呢?在循环的第一轮和第二轮中,print
语句里的a
竟然指向完全不同且无关的变量(假设真的有第二轮)?即使是只有一个元素的列表,它的工作方式也和不循环的代码不一样?说真的,这样下去简直是让人疯狂——光是想要记录和教这个,就可能让我想换一种编程语言。
那么,这种语言、它的开发者、老师、学习者和使用者,为什么要承受这么大的概念负担呢——去支持和鼓励半隐蔽、不明确使用全局变量?!这似乎并不是一个值得追求的目标,对吧?!