操作变量时UnboundLocalError会产生不一致的行为

时间:2015-04-21 18:23:06

标签: python scope

在Python中,以下代码有效:

a = 1
b = 2

def test():
    print a, b

test()

以下代码有效:

a = 1
b = 2

def test():
    if a == 1:
        b = 3
    print a, b

test()

但以下工作:

a = 1
b = 2

def test():
    if a == 1:
        a = 3
    print a, b

test()

最后一个块的结果是UnboundLocalError消息,说在分配之前引用了a

我知道如果我在global a定义中添加test(),我可以使最后一个块工作,因此它知道我正在谈论哪个a

b分配新值时,为什么不会出错?

我是否创建了一个本地b变量,它并没有对我大喊大叫,因为我在分配之前没有尝试引用它?

但如果是这样的话,为什么我可以print a, b在第一个块的情况下,而不必事先声明global a, b

3 个答案:

答案 0 :(得分:6)

让我给你一个明确提到的docs的链接。

  

如果变量 分配 在函数体内的任何位置都有一个新值, 就会假定它是本地的

(强调我的)

因此,您的变量a是一个局部变量,而不是全局变量。这是因为你有一个赋值语句

a = 3

在代码的第三行。这使a成为局部变量。并且在声明之前引用局部变量会导致错误UnboundLocalError

但是在第二个代码块中,您没有进行任何此类赋值语句,因此您不会收到任何此类错误。

另一个使用ful链接的是this

  

在函数或方法中引用 局部变量 时引发,但没有值绑定到该变量。

因此,您指的是在下一行中创建的局部变量。

为了防止这种情况,有两种方法

  • 好方法 - 传递参数

    将您的功能定义为def test(a):,并将其命名为test(a)

  • 糟糕的方式 - 使用global

    在函数调用的顶部有一行global a

Python范围规则有点棘手!你需要掌握它们来掌握语言。查看this

答案 1 :(得分:2)

修改a时,变为本地变量。当你只是引用它时,它是一个全球性的。您尚未在本地范围中定义a,因此无法对其进行修改。

如果要修改全局,则需要在本地范围内将其称为全局。

查看以下

的字节码
import dis

a = 9 # Global

def foo():
    print a # Still global

def bar():
    a += 1 # This "a" is local


dis.dis(foo)

输出:

  2           0 LOAD_GLOBAL              0 (a)
              3 PRINT_ITEM
              4 PRINT_NEWLINE
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE

对于第二个功能:

dis.dis(bar)

输出:

  2           0 LOAD_FAST                0 (a)
              3 LOAD_CONST               1 (1)
              6 INPLACE_ADD
              7 STORE_FAST               0 (a)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

第一个函数的字节码加载全局aLOAD_GLOBAL),因为它只被引用。第二个函数的字节码(LOAD_FAST尝试加载本地a,但尚未定义一个。

第二个功能有效的唯一原因是a等于1。如果a不是1,则b的本地分配不会发生,并且您会收到相同的错误。

答案 2 :(得分:2)

在第三个块中,编译器将a标记为本地变量,因为它被分配给它,因此当它在表达式中使用时,它将在本地范围中查找。由于它不存在,因此会引发异常。

在第二个块中,编译器将b标记为局部变量,但不是a,因此在访问a时没有异常,因为将搜索外部作用域。