Closure (闭包)
闭包 = 内层函数 + 捕获的外层变量(环境)
即:函数记住了定义时的作用域,而不是执行时的作用域。
def make_adder(x):
def adder(y):
return x + y # 使用外部函数的变量 x
return adder
add10 = make_adder(10)
print(add10(5)) # 15
✅ adder() 内部用到了外部函数 make_adder() 的变量 x
✅ 即使 make_adder() 已经执行完,adder() 仍然保留 x=10
这就是闭包。
要成为闭包,必须满足:
- 外层函数有局部变量
- 内层函数使用了这个局部变量
- 外层函数返回内层函数
例如:
def outer():
x = 100
def inner():
print(x)
return inner
def counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
c = counter()
print(c()) # 1
print(c()) # 2
def make_db_conn(url):
def query(sql):
print("connect:", url)
return f"run SQL: {sql}"
return query
db = make_db_conn("postgres://root")
db("SELECT 1")
def logger(fn):
def wrapper(*args, **kwargs):
print("calling:", fn.__name__)
return fn(*args, **kwargs)
return wrapper
def multiply(n):
return lambda x: x * n
double = multiply(2)
print(double(10)) # 20
Python 的函数对象有一个属性:__closure__
add10 = make_adder(10)
print(add10.__closure__)
输出类似:
(<cell at 0x...: int object at 0x...>,)
闭包中的变量储存在 cell 中,不会因为外层函数结束而销毁。
闭包中如果要修改外部变量,必须加 nonlocal:
def counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
否则 Python 会认为你在定义新的局部变量。
常见坑:
funcs = []
for i in range(5):
funcs.append(lambda: i)
print([f() for f in funcs])
输出:
[4, 4, 4, 4, 4]
因为 i 最终变成了 4。
正确方式:
funcs = []
for i in range(5):
funcs.append(lambda i=i: i)
from functools import partial
funcs = [partial(lambda x: x, i) for i in range(5)]
lambda 也是函数,所以也能产生闭包:
def make():
x = 3
return lambda y: x + y
闭包不是 lambda 的专属,也不是函数式编程特性,但在 Python 里非常常用。
比如你的爬虫里可以这样用:
def with_cache(fn):
cache = {}
def wrapper(x):
if x in cache:
return cache[x]
cache[x] = fn(x)
return cache[x]
return wrapper
@with_cache
def slow_fn(x):
time.sleep(1)
return x * 2
闭包让 cache 永远保留。