AI_Python基础-7.装饰器
·
Python 装饰器
标签: #Python #装饰器 #函数装饰器 #类装饰器
学习周期:1 天 | 核心目标:掌握装饰器原理,能独立编写函数装饰器、带参数装饰器和类装饰器
3.3 装饰器
装饰器(Decorator)是 Python 中极具实用性的语法糖,核心作用是 在不修改原函数/类代码、不改变其调用方式的前提下,为其增加额外功能(如日志记录、执行计时、权限校验、异常捕获等)。
其底层依赖 闭包 和 函数是一等对象 的特性(函数可作为参数传递、作为返回值返回,可赋值给变量),是“开闭原则”(对扩展开放、对修改关闭)的典型实现。
3.3.1 函数装饰器(基础)
核心逻辑
函数装饰器本质是一个函数,它接收一个函数(被装饰的原函数)作为参数,返回一个新的函数(增强后的函数),最终用新函数替代原函数。
三步实现
- 定义“装饰器函数”:接收原函数作为参数。
- 在装饰器函数内部,定义“新函数”(wrapper):实现增强功能 + 调用原函数。
- 装饰器函数返回“新函数”。
基础示例:日志装饰器
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"【日志】函数 {func.__name__} 开始执行...")
result = func(*args, **kwargs)
print(f"【日志】函数 {func.__name__} 执行结束!")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
add(1, 2) # 输出日志并返回 3
关键细节
*args, **kwargs:保证装饰器可接收任意参数,通用性强。- 必须返回原函数的结果,否则调用后会丢失返回值。
- 装饰器在
@语法时立即执行,原函数被替换为 wrapper。
保留原函数元信息(functools.wraps)
装饰后原函数的 __name__、__doc__ 会被覆盖,使用 functools.wraps 可解决。
import functools
def log_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
多个装饰器的执行顺序
装饰器叠加时,执行顺序从下往上(靠近函数的先执行)。
@decorator_a
@decorator_b
def func(): pass
# 等价于 func = decorator_a(decorator_b(func))
3.3.2 带参数的装饰器
当装饰器需要接收额外参数(如日志级别、重试次数)时,需要三层嵌套函数。
结构解析
def decorator_with_args(arg1, arg2):
def real_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 使用 arg1, arg2
return func(*args, **kwargs)
return wrapper
return real_decorator
@decorator_with_args("hello", 42)
def foo(): pass
实用示例:日志级别控制
def log(level="INFO"):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] 调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log(level="DEBUG")
def debug_func(): pass
权限校验装饰器
def permission_check(required_role):
def decorator(func):
@functools.wraps(func)
def wrapper(user_role, *args, **kwargs):
if user_role == required_role:
return func(*args, **kwargs)
else:
print(f"权限不足,需要 {required_role} 角色")
return None
return wrapper
return decorator
3.3.3 类装饰器
类装饰器通过实现 __init__ 和 __call__ 方法达到装饰效果,适合需要保存状态的复杂场景。
基础类装饰器
import functools
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"调用次数:{self.calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello(): pass
带参数的类装饰器
def repeat(times):
class RepeatDecorator:
def __init__(self, func):
self.func = func
self.times = times
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
for _ in range(self.times):
result = self.func(*args, **kwargs)
return result
return RepeatDecorator
@repeat(times=3)
def greet(): print("Hello")
类装饰器实现单例模式
class Singleton:
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@Singleton
class DatabaseConnection: pass
3.3.4 函数装饰器 vs 类装饰器
| 类型 | 实现方式 | 优势 | 适用场景 |
|---|---|---|---|
| 函数装饰器 | 两层/三层函数嵌套 | 实现简单、代码简洁 | 日志、计时、权限校验等简单场景 |
| 类装饰器 | 类 + __call__ 方法 |
可保存状态,适合复杂增强逻辑 | 调用计数、缓存、单例等需要状态的场景 |
📚 学习资料(Obsidian 可直接收藏)
🎯 学习建议(1 天计划)
- 上午:理解闭包概念,编写无参数装饰器(日志、计时),学会使用
functools.wraps。 - 下午:练习带参数的装饰器、类装饰器,掌握多个装饰器的执行顺序,完成缓存、重试等实战练习。
✅ 核心要点总结
- 装饰器本质:高阶函数,接收函数返回新函数,使用
@语法糖。 functools.wraps:必须使用,以保留原函数的元信息(__name__、__doc__)。- 带参数的装饰器:三层嵌套函数,最外层接收装饰器参数,内层接收函数,最内层接收函数参数。
- 类装饰器:通过
__init__接收被装饰函数(或参数),通过__call__实现包装逻辑,适合保存状态。 - 执行顺序:多个装饰器从下往上执行(靠近函数的最先被包装)。
- 应用场景:日志、计时、缓存、权限校验、重试、单例模式等。
练习题(自测)
- 编写一个装饰器
uppercase,将函数的返回值转换为大写字符串(假设返回值是字符串)。 - 编写一个带参数的装饰器
repeat(n),让被装饰函数重复执行n次。 - 使用类装饰器实现一个
Deprecated装饰器,当调用被装饰函数时打印警告“该函数已过时”。 - 写一个装饰器
memoize,为函数添加缓存功能(手动实现,不使用functools.lru_cache),支持任意参数。 - 使用装饰器为以下函数添加执行时间统计,并比较不同实现的性能差异:计算 1 到 1000000 的和(使用循环 vs 使用
sum)。
建议将上述代码在本地环境中运行,尝试修改参数并观察装饰器的行为。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)