作用域
在理解闭包前,先看一个函数:
def func():
in_func = 'In Func'
print(in_func)
func()
In Func
print(in_func)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-bbc477aa0551> in <module>()
----> 1 print(in_func)
NameError: name 'in_func' is not defined
可以看到func()函数内部的变量无法在函数外部使用
out_func = 'Out Func'
def func():
print(out_func)
func()
Out Func
print(out_func)
Out Func
全局变量out_func可以在函数外部和内部使用
闭包
def closure_func():
in_func = 'In Func'
def f():
return in_func
return f
ff = closure_func() # 此时ff=f
ff() # 此时ff()=f(),函数f()访问了外部函数的局部变量in_func
'In Func'
此时的ff就是一个闭包,包括了f()函数和自由变量in_func
其实在ff = closure_func()
语句结束后函数closure_func()生命周期就结束了,但是其局部变量in_func依然存在,因为它被闭包引用了,因此不会被回收。
维基百科对于闭包的解释
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
如果在一个内部函数里,对在外部作用域(但不在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量。
def make_power(x):
def f(y):
return y ** x
return f
power_two = make_power(2)
power_two(5)
25
power_two(3)
9
可以看到make_power()函数结束后,依然存在,被闭包引用。
所有函数都有一个 __closure__
属性,如果这个函数是一个闭包的话,那么它返回的是一个由 cell 对象 组成的元组对象。cell 对象的cell_contents 属性就是闭包中的自由变量。
power_two.__closure__
(<cell at 0x7f9154476918: int object at 0xa68a60>,)
power_two.__closure__[0].cell_contents #自由变量存储在了cell_contents中
2
创建一个闭包:
- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级namespace中的变量
- 闭包函数必须返回内嵌函数
闭包容易出现的错误
1.外部函数局部变量
def out_func():
out_var = 'Out Var'
def inner_func():
out_var = 'Inner Var'
return out_var
print('before: {} '.format(out_var))
inner_func()
print('after: {} '.format(out_var))
out_func()
before: Out Var
after: Out Var
闭包中函数无法修改外部函数的局部变量
要修改外部函数的局部变量,可是使用nonlocal关键字,修改上面的函数:
def out_func():
out_var = 'Out Var'
def inner_func():
nonlocal out_var
out_var = 'Inner Var'
return out_var
print('before: {} '.format(out_var))
inner_func()
print('after: {} '.format(out_var))
out_func()
before: Out Var
after: Inner Var
2.循环的问题
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
f1()
9
f2()
9
f3()
9
返回的函数并没有立刻执行,而是直到调用了f()才执行,在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
修改上述的函数:
def count():
def f(j):
return lambda: j*j
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1, f2, f3 = count()
f1()
1
f2()
4
f3()
9