闭包
闭包(closure)是由其他函数生成并返回的函数。即调用一个函数,这个函数返回一个在这个函数中定义的函数。
闭包最明显的特征就是被返回的函数可以访问其创建者局部命名空间中的对象,即使其创建者已经执行完毕,闭包仍然能继续访问其创建的局部命名空间。
下面举例说明:
def outside(x):
def inside(y):
return x+y
return inside
myfun=outside(1)
myfun(2)
# 3
myfun(10)
# 11
在这个例子中,返回的inside函数就是闭包。
可见函数闭包要满足以下条件
1.必须嵌套函数。
2.内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3.外部函数必须返回内嵌函数——必须返回那个内部函数
闭包的作用:可以保持程序上一次运行后的状态然后继续执行。
nonlocal
当有很多函数嵌套时,对象发挥作用的作用域可能会是其所嵌套函数的上一层,而不是全局,这时可以使用nonlocal语句来声明对象的该种作用域。
接着上面的例子,我们改变一个语句:
def outside(x):
def inside(y):
x=x+y # 改变这一句
return x
return inside
myfun=outside(1)
myfun(2)
不运的是报错了:
这是为什么呢?原因在于x=x+y
这一句,x=....
是在告诉python我要重新定义一个局部变量x, 这时pyhon就不会继承上一层函数中的x,并会把inside函数作用域的变量x清空。随后,..=x+y
中我们要用到x,因为x在inside作用域被清空了,所以python找不到x就报错了。
为了解决这个错误,我们只要告诉python在inside函数作用域中的x依然用outside函数作用域中的x即可,这个时候就要用到关键字nonlocal:
def outside(x):
def inside(y):
nonlocal x # 声明非局部变量
x=x+y # 改变这一句
return x
return inside
myfun=outside(1)
myfun(2)
# 3
这时,程序就正确了。
如果上面的x是一个list对象,情况就不一样了,例子如下:
def outside(z):
x=[z]
def inside(y):
x[0]=x[0]+y # 改变这一句
return x[0]
return inside
myfun=outside(1)
myfun(2)
# 3
这个例子正确的原因是,x[0]=...
意味着从x中取索引0的变量,不是重新定义的意思,因此python就向上一层函数的作用域中寻找列表x,这时就没有上面讲的问题了。
global
global和nonlocal很相似,都是从上一层获取变量。只不过,全局对象往往被声明在函数或其他程序块的外部,在函数内部若要声明为非局部变量要用global. 下面比较两个例子A,B:
例子A:
x=10
def outside(y):
nonlocal x
x=x+y
print(x)
outside(2)
例子B:
x=10
def outside(y):
global x
x=x+y
print(x)
outside(2)
# 12
其中例子A是报错的,例子B是正确的,原因在于outside外面没有函数了,已经属于全局变量的作用域了。
为了理解,我们再比较两个例子C,D:
例子C:
x=10
def outside():
def inside(y):
nonlocal x
x=x+y # 改变这一句
return x
return inside
myfun=outside()
myfun(2)
# 3
例子D:
x=10
def outside():
def inside(y):
global x
x=x+y # 改变这一句
return x
return inside
myfun=outside()
myfun(2)
# 12
其中例子C是错误的,例子D是正确的,就是因为x=10在全局作用域中定义的。
最后,我们再看两个比较难的例子E,F:
例子E:
x=10
def outside():
x=1
def inside(y):
global x
x=x+y # 改变这一句
return x
return inside
myfun=outside()
myfun(2)
# 12
例子F:
x=10
def outside():
x=1
def inside(y):
nonlocal x
x=x+y # 改变这一句
return x
return inside
myfun=outside()
myfun(2)
# 3
在例子E中我们利用global关键字从全局作用域中找到了x=10, 进而计算x+y时,x的值为10。 在例子F中,我们利用nonlocal关键字从上次函数中找到了x=1, 于是在计算x+y时 x的值是1.
总结
- 通常情况下,我们在函数中引用变量,如果该变量在该函数的作用域内重新定义了,就会用重新定义的变量。如果在该函数作用域内没有重新定义,就会自从向上层函数找同名变量。
- 按照1的规则程序一般不会出现问题,但是出现赋值和引用同时进行的语句时,例如x=x+y,python认为等号左边x是inside函数的作用域内重新定义的变量,等号右边x是outside函数的变量,这时就出现了矛盾,就报错了。为了处理这种情况,python出现了关键字nonlocal和global.
- nonlocal和global都是从外面调用变量来用,只是nonloca是向上一层函数找变量,global是从全部作用域中找变量。