操作变量时出现 UnboundLocalError 产生不一致的行为
UnboundLocalError when manipulating variables yields inconsistent behavior
在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
在赋值之前被引用。
我知道如果我在 test()
定义中添加 global a
可以使最后一个块工作,所以它知道我在说哪个 a
。
为什么我在给 b
赋新值时没有收到错误消息?
我是否创建了一个局部 b
变量,它不会对我大喊大叫,因为我没有在赋值之前尝试引用它?
但如果是这样的话,为什么我可以在第一个块的情况下 print a, b
,而不必事先声明 global a, b
?
当你修改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
第一个函数的字节码加载全局 a
(LOAD_GLOBAL
) 因为它只是被引用。第二个函数的字节码 (LOAD_FAST
) 尝试 加载本地 a
但尚未定义。
第二个函数起作用的唯一原因是 a
等于 1
。如果 a
不是 1
,则不会发生对 b
的本地分配,并且您会收到相同的错误。
让我给你 link 到 docs 明确提到的地方。
If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local.
(强调我的)
因此你的变量a
是一个局部变量而不是全局变量。这是因为你有一个赋值语句,
a = 3
在你代码的第三行。这使得 a
成为局部变量。并且在声明之前引用局部变量会导致错误,即 UnboundLocalError
.
但是在您的第二个代码块中,您没有进行任何此类赋值语句,因此您不会收到任何此类错误。
另一个有用的 link 是 this
Raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable.
因此您指的是您在下一行中创建的局部变量。
要防止这种情况有两种方法
好方法 - 传递参数
将函数定义为 def test(a):
并将其调用为 test(a)
错误的方式 - 使用 global
在函数调用的顶部有一行 global a
。
Python 范围规则有点棘手!你需要掌握它们才能掌握这门语言。查看 this
在第三个块中,编译器已将 a
标记为局部变量,因为它正在被赋值,因此当它在表达式中使用时,它会在局部范围内查找。由于那里不存在,因此引发异常。
在第二个块中,编译器将 b
标记为局部变量而不是 a
,因此在访问 a
时没有异常,因为将搜索外部范围。
在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
在赋值之前被引用。
我知道如果我在 test()
定义中添加 global a
可以使最后一个块工作,所以它知道我在说哪个 a
。
为什么我在给 b
赋新值时没有收到错误消息?
我是否创建了一个局部 b
变量,它不会对我大喊大叫,因为我没有在赋值之前尝试引用它?
但如果是这样的话,为什么我可以在第一个块的情况下 print a, b
,而不必事先声明 global a, b
?
当你修改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
第一个函数的字节码加载全局 a
(LOAD_GLOBAL
) 因为它只是被引用。第二个函数的字节码 (LOAD_FAST
) 尝试 加载本地 a
但尚未定义。
第二个函数起作用的唯一原因是 a
等于 1
。如果 a
不是 1
,则不会发生对 b
的本地分配,并且您会收到相同的错误。
让我给你 link 到 docs 明确提到的地方。
If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local.
(强调我的)
因此你的变量a
是一个局部变量而不是全局变量。这是因为你有一个赋值语句,
a = 3
在你代码的第三行。这使得 a
成为局部变量。并且在声明之前引用局部变量会导致错误,即 UnboundLocalError
.
但是在您的第二个代码块中,您没有进行任何此类赋值语句,因此您不会收到任何此类错误。
另一个有用的 link 是 this
Raised when a reference is made to a local variable in a function or method, but no value has been bound to that variable.
因此您指的是您在下一行中创建的局部变量。
要防止这种情况有两种方法
好方法 - 传递参数
将函数定义为
def test(a):
并将其调用为test(a)
错误的方式 - 使用
global
在函数调用的顶部有一行
global a
。
Python 范围规则有点棘手!你需要掌握它们才能掌握这门语言。查看 this
在第三个块中,编译器已将 a
标记为局部变量,因为它正在被赋值,因此当它在表达式中使用时,它会在局部范围内查找。由于那里不存在,因此引发异常。
在第二个块中,编译器将 b
标记为局部变量而不是 a
,因此在访问 a
时没有异常,因为将搜索外部范围。