用两个词来说,示例5和示例6之间的区别在于,示例5中的变量x
也被分配到相同的范围内,而示例6中没有。这触发了可以由历史原因理解的差异。
这引发了UnboundLocalError:
x = "foo"
def f():
print x
x = 5
f()
而不是打印“ foo”。即使一开始看起来很奇怪,这还是有道理的:函数f()在x
本地定义了变量,即使它在打印之后也是如此,因此x
在同一函数中对它的任何引用都必须是对该局部变量的引用。至少这是有道理的,如果您错误地在本地重用了全局变量的名称,并试图同时使用全局变量和局部变量,则可以避免意外的意外。这是一个好主意,因为这意味着我们可以静态地知道,只要看一眼变量,这 意味着变量。例如,我们知道这里print x
引用了局部变量(因此可能引发UnboundLocalError):
x = "foo"
def f():
if some_condition:
x = 42
print x
f()
现在,此规则不适用于类级范围:在那里,我们希望表达式x = x
能够正常工作,将全局变量捕获x
到类级范围中。这意味着类级别的作用域不遵循上面的基本规则:例如,我们不知道x
该作用域中是指某个外部变量还是本地定义的x
-–:
class X:
x = x # we want to read the global x and assign it locally
bar = x # but here we want to read the local x of the prevIoUs line
class Y:
if some_condition:
x = 42
print x # may refer to either the local x, or some global x
class Z:
for i in range(2):
print x # prints the global x the 1st time, and 42 the 2nd time
x = 42
因此,在类范围内,使用了不同的规则:通常会引发UnboundLocalError的地方—仅在这种情况下— 而是在模块全局变量中查找。仅此而已:它不遵循嵌套作用域链。
为什么不?我实际上怀疑有一个更好的解释是“出于历史原因”。用更专业的术语来说,它可以认为该变量x
既在类作用域中本地定义(因为已被分配给它), 又 应从父作用域作为词法嵌套变量传递(因为已被读取)。可以通过使用与LOAD_NAME
在本地作用域中查找的字节码不同的字节码来实现它,如果未找到,则退回到使用嵌套作用域的引用。
感谢wilberforce对http://bugs.python.org/issue532860的引用。如果我们认为毕竟应该修复该问题,那么我们可能有机会用提议的新字节码重新进行一些讨论(错误报告考虑终止对它的支持,x = x
但由于担心破坏太多现有代码而被关闭;相反,我是我建议这里将x = x
在更多情况下进行工作)。否则我可能会错过另一个要点…
似乎cpython在当前的3.4主干中确实做到了这一点:http ://bugs.python.org/issue17853 … …还是不?他们介绍字节码的原因略有不同,因此没有系统地使用它。