结论
global和nonlocal在局部声明变量,表明变量的定义不在块内,而在全局/上级局部作用域内。
global
先谈一下Python的引用查找循序:
先在当前局部作用域找声明,如果找不到,到上层(包括全局)找
举一个例子:
a = 1
def foo():
b = a
return b
print(foo())
程序输出 1。foo()内部没有定义1,它向上层找到了全局变量a。
稍微修改:
a = 1
def foo():
b = a
a = 2 # newline
return b
print(foo())
程序报错:UnboundLocalError: local variable 'a' referenced before assignment
因为这次a在foo()内部声明了,但使用先于声明,是错误的。
再改一下:
a = 1
def foo():
global a # newline
b = a
a = 2
return b
print(foo())
正常输出1。global关键字声明,foo()中的a即是全局变量a,后面的都是赋值语句。
nonlocal
nonlocal的功能和global类似,只是nonlocal声明的变量在上级局部作用域内,而不是全局定义。
上面的代码中,将global改为nonlocal:
a = 1
def foo():
nonlocal a
b = a
a = 1
return b
print(foo())
报错:SyntaxError: no binding for nonlocal 'a' found。表明nonlocal不能声明全局变量。
将foo包裹在上层局部作用域中:
def foo_upper():
a = 1
def foo():
nonlocal a
b = a
a = 1
return b
print(foo())
foo_upper()
正常输出1
nonlocal应用:闭包
利用nonlocal可以实现Python的闭包。
比如:
def adder():
num = 0
def add():
nonlocal num
num += 1
return num
return add
add = adder()
print(add())
print(add())
print(add())
print(add())
打印出了:1 2 3 4
或者,实现一个单例函数(不能同时执行多次)装饰器:
def singlecase(fn):
'装饰器:单例模式'
running = False
@functools.wraps(fn)
def wrapper(*args, **kargs):
nonlocal running
if running:
raise Exception('单例函数 ' + fn.__name__ + ' 已经在执行')
running = True
try:
return fn(*args, **kargs)
finally:
running = False
return wrapper