
【个人主页:玄同765】
大语言模型(LLM)开发工程师|中国传媒大学·数字媒体技术(智能交互与游戏设计)
深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调
技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️
工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案
专栏传送门:LLM大模型开发 项目实战指南、Python 从真零基础到纯文本 LLM 全栈实战、从零学 SQL + 大模型应用落地、大模型开发小白专属:从 0 入门 Linux&Shell
「让AI交互更智能,让技术落地更高效」
欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!
本文将带你彻底理解Python中闭包、装饰器和类装饰器的核心原理,并通过实战案例展示它们在真实项目中的应用。
一、闭包:函数的“记忆”能力
1. 什么是闭包?
闭包(Closure)是Python中一个强大的特性,指一个函数对象记住并访问其定义时的环境,即使该环境已经不再存在。
def outer(x):
def inner(y):
return x + y
return inner
add_5 = outer(5)
print(add_5(3)) # 输出 8
关键点:
inner函数引用了外部函数outer的变量x- 即使
outer已经执行完毕,add_5仍然记住x=5
2. 闭包的底层原理
Python使用作用域链实现闭包。当函数被调用时,Python会创建一个栈帧(stack frame),保存局部变量。闭包的特殊之处在于:
- 当闭包函数被返回时,它的栈帧不会被销毁
- 反而被保存在闭包对象中,形成一个闭包环境
3. 闭包的典型应用场景
- 工厂函数:创建具有特定行为的函数
- 状态保持:在函数间共享状态
- 延迟计算:将参数延迟到调用时
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter1 = counter()
print(counter1()) # 1
print(counter1()) # 2
二、装饰器:函数的“外挂”功能
1. 装饰器的本质
装饰器本质上是一个闭包,它接受一个函数作为参数,并返回一个新函数,在不修改原函数代码的前提下增强其功能。
2. 基础装饰器实现
def my_decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
执行结果:
Before function call
Hello!
After function call
3. 装饰器的语法糖
@my_decorator 是 say_hello = my_decorator(say_hello) 的语法糖,让代码更清晰。
4. 带参数的装饰器
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 会输出3次
三、类装饰器:用类实现装饰器
1. 为什么需要类装饰器?
当需要在装饰器中维护状态时,类装饰器比函数装饰器更合适。
2. 类装饰器实现原理
类装饰器需要实现__call__方法,使类的实例可以像函数一样被调用。
class Repeat:
def __init__(self, n):
self.n = n
def __call__(self, func):
def wrapper(*args, **kwargs):
for _ in range(self.n):
func(*args, **kwargs)
return wrapper
@Repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Bob")
3. 类装饰器 vs 函数装饰器
| 特性 | 函数装饰器 | 类装饰器 |
|---|---|---|
| 状态维护 | 需要nonlocal | 直接使用实例属性 |
| 可读性 | 简洁 | 适合复杂逻辑 |
| 调试 | 简单 | 可能需要查看类方法 |
| 适用场景 | 简单功能增强 | 需要状态管理的复杂场景 |
四、实战:在真实项目中应用
案例1:性能监控装饰器
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Done"
slow_function()
# 输出: slow_function took 1.0002 seconds
案例2:带状态的类装饰器(缓存)
class Cache:
def __init__(self, max_size=100):
self.cache = {}
self.max_size = max_size
self.order = []
def __call__(self, func):
def wrapper(*args):
key = str(args)
if key in self.cache:
return self.cache[key]
result = func(*args)
self.cache[key] = result
self.order.append(key)
# 维护缓存大小
if len(self.cache) > self.max_size:
del_key = self.order.pop(0)
del self.cache[del_key]
return result
return wrapper
@Cache(max_size=5)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 计算并缓存
print(fibonacci(10)) # 直接从缓存获取
案例3:Flask路由装饰器(真实框架应用)
# 模拟Flask的route装饰器
def route(path):
def decorator(func):
# 注册路由
print(f"Registered route: {path} -> {func.__name__}")
return func
return decorator
@route('/home')
def home():
return "Welcome to home!"
@route('/about')
def about():
return "About us"
五、常见误区与最佳实践
误区1:忘记使用functools.wraps
问题:装饰器会改变原函数的__name__和__doc__,影响调试和文档生成。
解决方案:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Before")
result = func(*args, **kwargs)
print("After")
return result
return wrapper
误区2:在类装饰器中忽略参数
问题:类装饰器的__init__接收装饰器参数,__call__接收被装饰函数。
正确做法:
class Decorator:
def __init__(self, n=1): # 接收装饰器参数
self.n = n
def __call__(self, func): # 接收被装饰函数
def wrapper(*args, **kwargs):
for _ in range(self.n):
func(*args, **kwargs)
return wrapper
最佳实践
- 优先使用函数装饰器:简单场景用函数装饰器
- 状态管理用类装饰器:需要维护状态时用类装饰器
- 始终使用
functools.wraps:保持函数元信息 - 避免过度装饰:保持代码可读性
六、总结
| 概念 | 核心 | 适用场景 |
|---|---|---|
| 闭包 | 函数记住定义环境 | 状态保持、工厂函数 |
| 装饰器 | 闭包的函数化应用 | 功能增强、日志、性能监控 |
| 类装饰器 | 用类实现装饰器 | 需要状态管理的复杂场景 |
关键洞察:
- 装饰器是闭包的典型应用,不是独立的概念
- 类装饰器是装饰器的扩展,不是替代
- 选择哪种装饰器取决于是否需要状态管理
记住:装饰器不是魔法,而是函数式编程思想在Python中的优雅实现。理解闭包是掌握装饰器的基础,而类装饰器则是处理复杂场景的利器。
附录:装饰器速查表
| 装饰器类型 | 语法 | 适用场景 |
|---|---|---|
| 简单装饰器 | @decorator | 日志、计时、权限检查 |
| 带参数的装饰器 | @decorator(arg) | 需要配置的装饰器(如重试次数) |
| 类装饰器 | @ClassDecorator() | 需要维护状态的装饰器(缓存、计数器) |
| 多层装饰器 | @decorator1 @decorator2 | 组合功能(如日志+缓存) |
254

被折叠的 条评论
为什么被折叠?



