有人能解释为什么下面的程序将失败:
def g(f):
for _ in range(10):
f()
def main():
x = 10
def f():
print x
x = x + 1
g(f)
if __name__ == '__main__':
main()
与消息:
Traceback (most recent call last):
File "a.py", line 13, in <module>
main()
File "a.py", line 10, in main
g(f)
File "a.py", line 3, in g
f()
File "a.py", line 8, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
但是,如果我只是改变变量x
到一个数组,它的工作原理:
def g(f):
for _ in range(10):
f()
def main():
x = [10]
def f():
print x[0]
x[0] = x[0] + 1
g(f)
if __name__ == '__main__':
main()
与输出
10
11
12
13
14
15
16
17
18
19
我很困惑的原因是,如果从f()
不能访问x
,为什么它可以访问时,如果x
是一个数组?
谢谢。
但这种回答说这个问题是与分配给X。 如果是这样的话,然后打印它应该工作得很好,应该不是吗?
你必须懂得在事情发生的顺序。 之前你的Python代码甚至是编译和执行,一些所谓的解析器通过Python代码读取和检查语法。 解析器做的另一件事是标志变量为本地。 当解析器看到在一个本地范围的代码的分配,在分配的左侧的变量被标记为本地。 在这一点上,没有什么更被编译,但 - 更不用说执行,因此没有分配发生; 可变仅仅标记为一个局部变量。
解析器完成后,该代码被编译并执行。 当执行到print语句:
def main():
x = 10 #<---x in enclosing scope
def f():
print x #<-----
x = x + 1 #<-- x marked as local variable inside the function f()
print语句看起来应该继续前进,在封闭范围(即“E”在LEGB查询过程)打印X。 然而,因为解析器先前标记x作为内部为f的本地变量(),蟒不进行过去本地范围(中LEGB查找过程“L”)查找X。 因为x尚未在“打印X”执行时间局部范围分配,蟒蛇吐出一个错误。
需要注意的是即使一个地方发生的分配将永远不会执行的代码,解析器还是标志着一个任务作为一个局部变量的左边的变量。 解析器没有的事情将如何执行的想法,所以一味的搜索语法错误和局部变量的整个文件 - 即使是在代码不能执行。 下面是一些例子:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
a b c #...yet the parser finds a syntax error here
return f
f = dostuff()
f()
--output:--
File "1.py", line 8
a b c
^
SyntaxError: invalid syntax
标志着局部变量时,解析器做同样的事情:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
x = 0 #..yet the parser marks x as a local variable
return f
f = dostuff()
f()
现在看,当你执行这最后的程序发生了什么:
Traceback (most recent call last):
File "1.py", line 11, in <module>
f()
File "1.py", line 4, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
当语句“打印X”执行,因为解析器标x作为一个局部变量x的查找停止在局部范围。
这“功能”是不是唯一的蟒蛇 - 它发生在其他语言中也。
至于数组例如,当你写:
x[0] = x[0] + 1
告诉蟒蛇去查找了一个数组命名为x和分配的东西给它的第一个元素。 因为没有分配给名为x在局部范围内任何东西,解析器并不标志着x作为一个局部变量。
其原因是在第一示例中,您使用的分配操作, x = x + 1
,所以函数被定义时蟒以为x
是局部变量。 但是,当你真正调用的函数蟒蛇未能找到任何的价值x
本地的RHS,所以提出了一个错误。
在你的第二个例子,而不是分配你只需改变一个可变的对象,所以Python将永远不会提出任何异议,并取x[0]
从封闭范围内的值(实际上它首先查找它在封闭的范围,那么全球范围终于在建宏,但一旦停止,因为被发现)。
在Python 3倍,你可以使用外地关键字进行处理并在py2x您可以将值传递给内部函数或使用功能属性。
使用功能属性:
def main():
main.x = 1
def f():
main.x = main.x + 1
print main.x
return f
main()() #prints 2
明确地传递值:
def main():
x = 1
def f(x):
x = x + 1
print x
return x
x = f(x) #pass x and store the returned value back to x
main() #prints 2
使用nonlocal
在py3x:
def main():
x = 1
def f():
nonlocal x
x = x + 1
print (x)
return f
main()() #prints 2
问题是,变量x
被关闭回升。 当你试图给正在从封闭拿起一个变量,蟒蛇会抱怨,除非你使用global
或nonlocal
1个关键字。 在您使用的情况list
,你并没有使用到的名字-您可以修改它得到了在封闭拿起一个对象,但你不能分配给它。
基本上,发生错误的print x
线,因为当蟒解析功能,它把该x
到则认为被指定x
必须是一个局部变量。 当你到行print x
,蟒蛇尝试查找本地x
,但它不存在。 这可以通过使用可以看出dis.dis
检查字节码。 在这里,Python使用LOAD_FAST
它用于本地变量,而不是指令LOAD_GLOBAL
这是用于非局部变量的指令。
通常情况下,这会导致一个NameError
,但蟒蛇尝试多一点有益通过寻找x
在func_closure
或func_globals
2。 如果发现x
在其中的一个,它会引发UnboundLocalError
,而不是给你什么正在发生一个更好的主意-你有可能无法找到一个局部变量(不是“绑定”)。
只有1个 python3.x
2 python2.x -在python3.x,这些属性已经改变到__closure__
和__globals__
分别
问题是在该行
x = x + 1
这是第一次x
被分配在函数f()
告诉编译器, x
是一个本地名。 它与冲突前行print x
,它无法找到本地以前设置的x
。 这就是你的错误UnboundLocalError: local variable 'x' referenced before assignment
从何而来。
需要注意的是,当编译器试图找出哪些对象错误发生x
在print x
指。 因此, print x
不执行。
将其更改为
x[0] = x[0] + 1
没有新的名称添加。 所以编译器知道你指的是阵列外部f()