在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
?
答案 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
第一个函数的字节码加载全局a
(LOAD_GLOBAL
),因为它只被引用。第二个函数的字节码(LOAD_FAST
)尝试加载本地a
,但尚未定义一个。
第二个功能有效的唯一原因是a
等于1
。如果a
不是1
,则b
的本地分配不会发生,并且您会收到相同的错误。
答案 2 :(得分:2)
在第三个块中,编译器将a
标记为本地变量,因为它被分配给它,因此当它在表达式中使用时,它将在本地范围中查找。由于它不存在,因此会引发异常。
在第二个块中,编译器将b
标记为局部变量,但不是a
,因此在访问a
时没有异常,因为将搜索外部作用域。