Python本地变量与全局变量

11

我理解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()
4个回答

12

设置和测试

为了分析您的问题,让我们创建两个单独的测试函数来复制您的问题:

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操作上。


6

在你提交的函数代码中,Python不是逐行执行的。它首先将其解析为一个执行块。根据变量是在(函数)本地级别上编写的来决定其是局部变量还是全局变量。由于这种情况发生了,它决定该变量是局部变量,因此出现了错误。


4
这是因为在Python中,你需要明确地告诉它你要修改一个全局变量的值。你可以这样做:
def test():
  global a
  print a  #line 4, Error : local variable 'a' referenced before assignment
  a=0      #line 5

使用global a,你可以修改函数中的变量。 你可能想看一下这个链接:


3

简短解释:

变量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        

对以上不清楚的部分进行解释:

  • LOAD_FAST表示将a的引用推送到堆栈上。
  • STORE_FAST表示Python将0(来自LOAD_CONST)赋值给本地变量a

因此,问题在于LOAD_FAST在STORE_FAST之前执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接