我理解Python中本地变量和全局变量的概念,但是我对以下代码为什么会出现这种错误有疑问。Python逐行执行代码,因此直到读取第5行代码之前,它并不知道a是一个局部变量。Python会在尝试执行第5行代码后返回一行并将其标记为错误吗?
a=0
def test():
print a #line 4, Error : local variable 'a' referenced before assignment
a=0 #line 5
test()
我理解Python中本地变量和全局变量的概念,但是我对以下代码为什么会出现这种错误有疑问。Python逐行执行代码,因此直到读取第5行代码之前,它并不知道a是一个局部变量。Python会在尝试执行第5行代码后返回一行并将其标记为错误吗?
a=0
def test():
print a #line 4, Error : local variable 'a' referenced before assignment
a=0 #line 5
test()
设置和测试
为了分析您的问题,让我们创建两个单独的测试函数来复制您的问题:
a=0
def test1():
print(a)
test1()
输出0
。因此,调用这个函数没有问题,但在下一个函数中:
def test2():
print(a) # Error : local variable 'a' referenced before assignment
a=0
test2()
我们遇到了一个错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment
反汇编
我们可以对这两个函数进行反汇编(先是Python 2):
>>> import dis
>>> dis.dis(test1)
2 0 LOAD_GLOBAL 0 (a)
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
我们可以看到第一个函数自动加载了全局变量a
,而第二个函数:
>>> dis.dis(test2)
2 0 LOAD_FAST 0 (a)
3 PRINT_ITEM
4 PRINT_NEWLINE
3 5 LOAD_CONST 1 (0)
8 STORE_FAST 0 (a)
11 LOAD_CONST 0 (None)
14 RETURN_VALUE
由于在其中分配了a
,因此尝试从locals中进行LOAD_FAST(作为优化的一部分,因为函数在运行之前已被预编译为字节码。)
如果我们在 Python 3 中运行这个代码段,我们会看到几乎相同的效果:
>>> test2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment
>>>
>>> import dis
>>> dis.dis(test1)
2 0 LOAD_GLOBAL 0 (print)
3 LOAD_GLOBAL 1 (a)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis() # disassembles the last stack trace
2 0 LOAD_GLOBAL 0 (print)
--> 3 LOAD_FAST 0 (a)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
3 10 LOAD_CONST 1 (0)
13 STORE_FAST 0 (a)
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
我们发现问题再次出现在LOAD_FAST操作上。
在你提交的函数代码中,Python不是逐行执行的。它首先将其解析为一个执行块。根据变量是在(函数)本地级别上编写的来决定其是局部变量还是全局变量。由于这种情况发生了,它决定该变量是局部变量,因此出现了错误。
def test():
global a
print a #line 4, Error : local variable 'a' referenced before assignment
a=0 #line 5
使用global a
,你可以修改函数中的变量。
你可能想看一下这个链接:
简短解释:
变量a
绑定到test
函数。当你尝试打印它时,Python会抛出错误,因为局部变量a
稍后才会初始化。但是如果你删除a=0
,代码将正常执行并输出0。
更详细的解释:
Python解释器从局部范围开始搜索名为a
的变量。它发现a
确实在函数内声明,并且只在第五行初始化。一旦找到,查找就结束了。
当它尝试处理print a
(第四行)时,它会说:“哦,我需要打印一个还没有初始化的变量,最好抛出一个错误。”
基于函数字节码的解释
如果您运行此代码:
import dis
a = 0
def test():
print a #line 4, Error : local variable 'a' referenced before assignment
a = 0 #line 5
dis.dis(test)
6 0 LOAD_FAST 0 (a)
3 PRINT_ITEM
4 PRINT_NEWLINE
7 5 LOAD_CONST 1 (0)
8 STORE_FAST 0 (a)
11 LOAD_CONST 0 (None)
14 RETURN_VALUE
对以上不清楚的部分进行解释:
a
的引用推送到堆栈上。a
。因此,问题在于LOAD_FAST在STORE_FAST之前执行。