闭包形成条件
- 在函数中定义函数。分别称为:
- 外部函数:Enclosing function
- 内部函数:Enclosed function,Inner function,Nested function
- 外部函数的返回值是内部函数
- 内部函数使用了外部函数的变量,称为自由变量
自由变量
内部函数(外部函数的返回值)会被延迟执行,执行内部函数需要用到外部函数定义的变量,那么就只有将这些(自由)变量保存起来;又Python万物皆对象,函数也是一个对象,因此就将这些自由变量保存到内部函数的成员变量__closure__
__closure__
是一个tuple,包含内部函数需要的所有外部函数的变量(自由变量)
def outer(x):
a = 5
b = 2 # 没有被使用,也就没保存到__closure__里面
def inner():
print(x, a)
return inner
closure_f = outer(1)
closure_f() # 1 5
# 获取自由变量名
print(len(closure_f.__code__.co_freevars)) # 2
print(closure_f.__code__.co_freevars[0]) # 'a'
print(closure_f.__code__.co_freevars[1]) # 'x'
# 获取自由变量值,index与变量名一致
print(len(closure_f.__closure__)) # 2
print(closure_f.__closure__[0].cell_contents) # 5
print(closure_f.__closure__[1].cell_contents) # 1
如果外部函数返回多个内部函数,这些内部函数共享(浅拷贝)一个__closure__
,也就是说,其中一个函数修改了自由变量,其他函数访问的自由变量也会跟着改变
def outer(x):
funcs = []
for a in range(x):
def inner():
nonlocal a
a += 1
return a
funcs.append(inner)
return funcs
closure_f = outer(3)
print([f() for f in closure_f]) # -> [3, 4, 5]
修改自由变量
这个规则跟全局变量很相似,只不过global
用于全局变量,nonlocal
用于嵌套函数的外部变量(表明该变量即不在全局作用域也不在局部作用域)
-
不可变类型(int, str等)不能直接修改,要要加上
nonlocal
修饰才能修改 -
可变类型(list等)可以修改其内容,如
append
,+=
等
def outer(x):
a = [5]
def inner():
nonlocal x
print(x, a)
x += 1
a[0] += 1
return inner
closure_f = outer(1)
closure_f() # 1 5
closure_f() # 2 6
closure_f() # 3 7
Python Inner Functions: What Are They Good For? – Real Python