python中的闭包函数
1、闭包的核心条件(以 Python 为例)
1.1、要形成闭包,必须满足三个条件:
- 存在嵌套函数(内部函数定义在外部函数内部);
- 内部函数引用了外部函数作用域中的变量(注意非全局变量);
- 外部函数返回内部函数对象(而非内部函数的执行结果)。
1.2、直接看代码
def outer_func(out_var):
# 外部函数定义变量 out_var
def inner_func():
# 内部函数引用外部函数的变量
print(f"我能访问外部函数的变量:{out_var}")
# 外部函数返回内部函数(不执行,只返回函数对象)
return inner_func
# 调用外部函数,得到内部函数对象,此时 outer_func 已执行完毕
closure = outer_func("闭包测试变量")
# 调用内部函数,依然能访问外部函数的变量
closure() # 输出:我能访问外部函数的变量:闭包测试变量
我们一步一步看,先看闭包函数的定义:
def outer_func(out_var): # 定义了外部函数
# 外部函数定义变量 out_var
def inner_func():
# 内部函数引用外部函数的变量
print(f"我能访问外部函数的变量:{out_var}")
# 外部函数返回内部函数(不执行,只返回函数对象)
return inner_func
调用过程中,先直接调用外部函数,然后因为外部函数返回了内部函数的函数名,所以此时使用closure也能调用 inner_func函数。
我们再来看一下另外一个例子:

这个例子,是内层和外层函数都需要传入参数,并且这两个参数都需要再内层函数中使用
2、闭包应用的实际场景
场景 1:轻量级 “无类封装”(替代简单类)
如果只需要封装少量状态(1-2 个变量),不需要定义完整的类,闭包是更简洁的选择:
对比:类 vs 闭包
python
# 用类实现(繁琐)
class Printer:
def __init__(self, prefix):
self.prefix = prefix
def print_msg(self, msg):
print(f"[{self.prefix}] {msg}")
info_printer = Printer("INFO")
info_printer.print_msg("程序启动")
# 用闭包实现(简洁)
def make_printer(prefix):
def printer(msg):
print(f"[{prefix}] {msg}") # 保留prefix状态
return printer
info_printer = make_printer("INFO")
info_printer("程序启动") # 调用更简单,无需写.print_msg()
工程价值:减少模板代码,轻量级封装状态,代码更简洁。
🔍 场景 2:实现装饰器(Python 最核心的应用)
装饰器是闭包最经典的实战场景,用于「无侵入增强函数功能」(日志、缓存、权限校验、计时等):
示例:带缓存的装饰器(避免重复计算)
python
运行
def cache_decorator(func):
cache = {} # 闭包保留的缓存状态,所有调用共享
def wrapper(*args):
if args not in cache:
cache[args] = func(*args) # 首次计算并存缓存
return cache[args]
return wrapper
@cache_decorator
def calc_sum(n):
# 模拟耗时计算:1+2+...+n
return sum(range(n+1))
# 第一次计算,存入缓存
print(calc_sum(1000000))
# 第二次直接取缓存,无需重复计算
print(calc_sum(1000000))
其实这里的信息量还是很大的,关键要弄清楚cache字典存在的周期:cache 的生命周期 = wrapper 函数的生命周期,只要 wrapper 还能被访问到,cache 就活着;wrapper 被回收,cache 就跟着走。
工程价值:
- 无侵入增强:原函数
calc_sum无需修改; - 状态复用:缓存
cache被所有调用共享,避免重复开销。
🔍 场景 3:实现 “有状态的函数”(替代全局变量)
如果函数需要记住之前的执行状态,但不想用全局变量(全局变量易污染、难维护),闭包是最优解:
示例:计数器(记住累计次数)
python
def make_counter():
count = 0 # 闭包保留的状态,外部无法直接修改
def counter():
nonlocal count # 声明修改外层变量
count += 1
return count
return counter
# 两个计数器独立保留状态
counter1 = make_counter()
counter2 = make_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter2()) # 1(和counter1互不干扰)
工程价值:
- 避免全局变量:
count只在闭包内可见,不会被其他代码意外修改; - 状态隔离:每个计数器独立保留自己的
count,互不干扰。 - 注意这里的关键字nonlocal,声明后,在内部函数中修改外部函数中定义的变量变量
🔍 场景 4:延迟执行 / 惰性加载(提升程序性能)
闭包可以把 “计算逻辑” 和 “执行时机” 分开,实现按需加载:
示例:懒加载配置文件(首次使用时才读取)
python
def lazy_load_config(file_path):
config = None # 初始为None,不加载
def load_config():
nonlocal config
if config is None:
# 首次调用时才读取文件(延迟执行)
with open(file_path, "r") as f:
config = f.read()
return config
return load_config
# 初始化时不读取文件,提升启动速度
config_loader = lazy_load_config("config.ini")
# 只有第一次使用时才读取文件
print(config_loader())
# 后续调用直接返回缓存的config
print(config_loader())
工程价值:避免程序启动时加载大量不必要的资源,提升启动速度。
🔍 场景 5:实现策略模式(动态切换业务逻辑)
闭包可以封装不同的业务策略,根据场景返回不同的策略函数,且保留策略的配置状态:
示例:动态生成不同的校验规则
python
def make_validator(min_len, max_len):
# 封装校验规则的配置(min_len/max_len)
def validator(text):
return min_len <= len(text) <= max_len
return validator
# 生成不同的校验器:手机号校验(11位)、密码校验(6-16位)
phone_validator = make_validator(11, 11)
pwd_validator = make_validator(6, 16)
print(phone_validator("13800138000")) # True
print(pwd_validator("123456")) # True
print(pwd_validator("123")) # False
工程价值:一次定义规则模板,多次生成不同配置的校验器,代码复用性高。
🔍 场景 6:异步 / 回调函数(保留上下文)
在异步编程、事件监听中,闭包可以保留回调函数的上下文(比如请求参数、用户信息):
示例:异步请求的回调(保留请求 ID)
python
import time
import threading
def async_request(url):
request_id = f"req-{int(time.time())}" # 生成唯一请求ID
def callback(response):
# 闭包保留request_id,回调时能关联请求和响应
print(f"请求[{request_id}] {url} 响应:{response}")
# 模拟异步执行:启动线程处理请求
threading.Thread(target=lambda: callback("success")).start()
# 发起两个异步请求,回调能正确关联各自的request_id
async_request("https://api.example.com/user")
async_request("https://api.example.com/order")
工程价值:回调函数能记住发起请求时的上下文(request_id/url),避免用全局变量传递状态。
🎯 闭包的核心优势(工程视角)
表格
| 优势 | 对比方案 | 价值 |
|---|---|---|
| 轻量级封装 | 类 | 少写 __init__/self 等模板代码,简洁高效 |
| 状态隔离 | 全局变量 | 状态只在闭包内可见,避免污染和意外修改 |
| 延迟执行 | 立即执行 | 按需加载资源,提升程序启动速度和运行效率 |
| 无侵入增强 | 修改原函数 | 装饰器模式,不破坏原函数逻辑,符合开闭原则 |
总结
- 闭包的核心是保留外层函数的状态,且状态对外隔离;
- 工程中最常用的场景:装饰器、轻量级状态封装、延迟执行、策略模式、异步回调;
- 适用场景:需要 “函数级别的状态复用”,但又不想定义类 / 用全局变量的场景。
更多推荐




所有评论(0)