在Python中,一个变量的scope范围从小到大分成4部分:Local Scope(也可以看成是当前函数形成的scope),Enclosing Scope(简单来说,就是外层函数形成的scope),Global Scope(就是当前文件形成的scope),Builtins Scope(简单来说,就是Python内置的变量位于最顶层的scope)。当Python开始查找一个非限定的变量名时(像obj.attr中的attr,就是一个被限定的变量名字,它被限定在obj对象中,而普通的变量名就是没有限定的),总是从当前变量名所处的scope开始,顺着前面提到的scope链开始往上查找,一旦查找到就不会往上再继续查找,如果查找完整个scope链还是没找到,Python会报错。
上面提到的变量名查找顺序,可以简单的记为LEGB(每一个scope的首字母),而global和nonlocal,可以改变查找顺序。
global
在文件中声明的变量自动成为global的,而如果想在一个函数里面声明一个全局变量,就需要使用global关键字:
global var1, var2, ... # 多个变量用逗号隔开
对于global关键字,需要注意以下几点:
1 当Python看到一个变量由global变量声明,开始查找的scope不是从这个变量当前所在的scope开始查找,而是从Global Scope开始查找;如果Global Scope没有找到,就会继续到Builtins Scope查找;
2 global关键字声明的对象允许赋值,如果这个变量之前不存在,那么,这次赋值就是创建了一个全局变量;如果这个变量之前存在,那么,这次赋值就改变了这个全局变量的值:
def test(): global x x = 1 # x之前不存在,因此在Global Scope创建了一个全局变量x x = 1 def test(): global x x = 99 # x之前在Global Scope中已经存在,因此这里是改变x的值
3 只要被global关键字声明的变量,都会成为全局变量,如果该变量原来不是全局变量,也会如此,并且,如果之前Global Scope里面有同名变量,那么被global声明的变量会取代这个同名变量:
x = 99 def test(): x = 88 global x # 这样做的话Python会产生警告:SyntaxWarning: name 'x' is assigned to before global declaration >>>print(x) # 一开始访问的是全局变量x >>>99 >>>test() # 执行test函数之前,原本函数里面的局部变量x成为了全局变量,并取代原来的全局变量 >>>print(x) # 现在访问的是取代后的全局变量x,值变为88 >>>88 # 在看一个例子,加入开始没有定义全局变量x def test(): x = 88 global x # 仍会产生相同的警告 >>>test() >>>print(x) # 打印结果为88!!! >>>88
nonlocal
nonlocal是Python 3.X加入进来的关键字,Python 2.X中没有。在Python中,嵌套函数是可以访问外部函数的变量的(至少在>Python 2.2的版本是这样的,在Python 2.2之前的版本中,变量的查找从当前函数开始,然后直接到Global Scope,Builtins Scope,跳过了外层函数),但是却不可以改变外部函数变量的值,如果确实要改变,就的使用nonlocal变量进行声明:
nonlocal var1, var2,... # 只在Python 3.X中支持,多个变量用逗号隔开
对于nonlocal关键字,需要注意以下几点:
1 nonlocal关键字只在Python 3.X中支持,Python 2.X没有这个关键字;
2 nonlocal关键字只可以在函数内部使用,在其他地方使用会报错;
3 nonlocal声明的变量,之前必须已经存在(并且是在外部函数中存在),如果变量不存在就对这个变量赋值,会报错,这点和global关键字不一样:
# 变量存在于Global Scope x = 99 def test(): nonlocal x # 报错:SyntaxError: name 'x' is assigned to before nonlocal declaration # 变量存在于当前函数 def test(): x = 99 nonlocal x # 报错:SyntaxError: name 'x' is assigned to before nonlocal declaration
4 对于nonlocal声明的变量,只会在外部函数中查找该变量,不会在Global Scope和Builtins Scope中查找