Python 闭包函数(Closure)分类精讲 CookBook 讲义
·
一、闭包基础概念
1.1 什么是闭包 —— 三要素
脚本:01_closure_basic.py
def outer(x):
def inner(y):
return x + y # ① 引用外部变量 x
return inner # ② 返回内函数
add_5 = outer(5) # ③ 外函数已结束,但 x 被保留
print(add_5(3)) # 8
print(add_5.__closure__) # 查看 cell 对象
知识点:内函数引用外函数局部变量 + 外函数返回内函数 + 外函数已结束但变量存活。
二、闭包 vs 普通函数 / 全局变量
2.1 对比全局变量 —— 避免污染
脚本:02_vs_global.py
# 全局变量方案
counter = 0
def inc_global():
global counter; counter += 1; return counter
# 闭包方案(无全局)
def make_counter():
count = 0
def inc():
nonlocal count; count += 1; return count
return inc
c = make_counter()
for i in range(3):
print("global:", inc_global(), "closure:", c())
结论:闭包封装状态,不污染全局命名空间。
三、nonlocal 与变量修改
3.1 只读陷阱 —— 未声明 nonlocal
脚本:03_readonly_trap.py
def make_adder(x):
def adder(y):
# x += y # UnboundLocalError
return x + y
return adder
add5 = make_adder(5)
print(add5(3)) # 8
3.2 nonlocal 修改外部变量
脚本:04_nonlocal.py
def make_adder(x):
def adder(y):
nonlocal x
x += y
return x
return adder
add5 = make_adder(5)
print(add5(3)) # 8
print(add5(2)) # 10 状态被保留
四、参数与默认值陷阱
4.1 默认值列表陷阱
脚本:05_default_list_trap.py
# 错误:共享同一份列表
def append(x, lst=[]):
lst.append(x); return lst
print(append(1)) # [1]
print(append(2)) # [1, 2] 意外共享!
# 闭包方案:每次新列表
def make_appender():
lst = []
def append(x):
lst.append(x); return lst
return append
app = make_appender()
print(app(1)) # [1]
print(app(2)) # [1, 2] 正常
五、多层嵌套闭包
5.1 三层嵌套
脚本:`06_nested_closure.py"
def f1(a):
def f2(b):
def f3(c):
return a + b + c
return f3
return f2
add = f1(10)(5) # a=10, b=5
print(add(3)) # 18 (a+b+c)
六、闭包 + 装饰器
6.1 手写计时装饰器(闭包保存原函数)
脚本:`07_timer_decorator.py"
import time, functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_add(a, b):
time.sleep(0.5); return a + b
print(slow_add(3, 4))
知识点:wrapper 是闭包,保存 func 引用。
七、闭包 vs 类(状态封装)
7.1 用闭包实现计数器
脚本:`08_closure_counter.py"
def make_counter(start=0):
count = start
def inc(step=1):
nonlocal count
count += step
return count
def dec(step=1):
nonlocal count
count -= step
return count
return inc, dec
inc, dec = make_counter(10)
print(inc()) # 11
print(dec()) # 10
7.2 同等功能用类实现
脚本:`09_class_counter.py"
class Counter:
def __init__(self, start=0):
self.count = start
def inc(self, step=1):
self.count += step
return self.count
def dec(self, step=1):
self.count -= step
return self.count
c = Counter(10)
print(c.inc()) # 11
print(c.dec()) # 10
对比:闭包轻量、无类实例开销;类更易扩展、继承。
八、性能与陷阱
8.1 闭包 vs 类访问速度
脚本:`10_speed_closure_vs_class.py"
import timeit
def make_adder(x):
def add(y):
return x + y
return add
class Adder:
def __init__(self, x):
self.x = x
def __call__(self, y):
return self.x + y
add_clos = make_adder(5)
add_inst = Adder(5)
t1 = timeit.timeit('add_clos(3)', globals=globals(), number=10000000)
t2 = timeit.timeit('add_inst(3)', globals=globals(), number=10000000)
print("closure:", t1, "class:", t2) # 闭包略快(无 self 解析)
8.2 循环变量陷阱
脚本:`11_loop_closure_trap.py"
# 错误:全部绑定到同一个 i
funcs = [lambda x: x + i for i in range(3)]
print([f(10) for f in funcs]) # [12, 12, 12] i=2
# 正确:默认参数捕获当前 i
funcs_fix = [lambda x, i=i: x + i for i in range(3)]
print([f(10) for f in func_fix]) # [10, 11, 12]
九、综合案例
9.1 可重用计时器(带重置)
脚本:`12_timer_with_reset.py"
def make_timer():
import time
start = time.perf_counter()
def elapsed():
return time.perf_counter() - start
def reset():
nonlocal start
start = time.perf_counter()
return elapsed, reset
elapsed, reset = make_timer()
time.sleep(0.1)
print("elapsed:", elapsed())
reset(); time.sleep(0.05)
print("after reset:", elapsed())
9.2 记忆化(缓存)闭包
脚本:`13_memoize_closure.py"
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
print(fib(50)) # 秒出
9.3 配置封装(嵌套 dict + 闭包)
脚本:`14_config_closure.py"
def make_config(defaults):
cfg = dict(defaults)
def get(key):
return cfg[key]
def set(key, value):
cfg[key] = value
def all():
return cfg.copy()
return get, set, all
get, set, all = make_config({"host": "localhost", "port": 8000})
set("port", 9000)
print("config:", all())
更多推荐

所有评论(0)