Day 27 训练
Day 27 训练
- Python装饰器详解:从原理到实践,深入理解语法糖
- 一、装饰器原理揭秘:优雅的语法糖背后
- 1.1 装饰器的核心执行流程
- 1.2 装饰器结构剖析
- 二、进阶实战:处理带参数的函数
- 2.1 可变参数解决方案
- 2.2 参数处理要点
- 三、实战案例:日志记录装饰器
- 3.1 完整实现代码
- 3.2 执行结果演示
- 3.3 常见错误示例
- 四、装饰器开发最佳实践
- 4.1 关键注意事项
- 4.2 调试技巧
- 五、扩展应用场景
Python装饰器详解:从原理到实践,深入理解语法糖
一、装饰器原理揭秘:优雅的语法糖背后
1.1 装饰器的核心执行流程
装饰器的本质是一个高阶函数,其执行流程可以分解为以下步骤:
def display_time(func): # 装饰器函数def wrapper():t1 = time.time()func() # 调用原函数t2 = time.time()print(f"耗时: {t2-t1}秒")return wrapper@display_time # 等价于 prime_nums = display_time(prime_nums)
def prime_nums(): # 被装饰函数# 计算素数代码...prime_nums() # 实际调用的是wrapper()
关键点解析:
- 函数替换:装饰后原函数名指向wrapper函数
- 执行时机:@语法在模块加载时立即执行
- 隐式传递:Python自动传递被装饰函数给装饰器
1.2 装饰器结构剖析
组成部分 | 作用说明 |
---|---|
外层装饰器函数 | 接收被装饰函数,返回包装函数 |
内层包装函数 | 实现功能扩展,最终调用原函数 |
@语法糖 | 简化装饰器应用语法 |
二、进阶实战:处理带参数的函数
2.1 可变参数解决方案
当被装饰函数需要接收参数时,需要使用*args和**kwargs:
def display_time(func):def wrapper(*args, **kwargs): # 接收任意参数t1 = time.time()result = func(*args, **kwargs) # 解包参数print(f"耗时: {time.time()-t1}秒")return result # 返回原函数结果return wrapper@display_time
def add(a, b):return a + bprint(add(3, 5)) # 输出:8 和耗时信息
2.2 参数处理要点
- 参数透传:wrapper必须与被装饰函数参数兼容
- 返回值处理:必须显式返回原函数结果
- 参数类型:
- *args:接收位置参数元组
- **kwargs:接收关键字参数字典
三、实战案例:日志记录装饰器
3.1 完整实现代码
def logger(func):"""记录函数执行日志的装饰器"""def wrapper(*args, **kwargs):print(f"[开始] {func.__name__},参数: {args}, {kwargs}")result = func(*args, **kwargs)print(f"[结束] {func.__name__},返回值: {result}")return resultreturn wrapper@logger
def multiply(a, b):return a * b# 测试不同调用方式
multiply(2, 3) # 位置参数
multiply(a=2, b=3) # 关键字参数
multiply(2, b=3) # 混合参数
3.2 执行结果演示
[开始] multiply,参数: (2, 3), {}
[结束] multiply,返回值: 6
[开始] multiply,参数: (), {'a': 2, 'b': 3}
[结束] multiply,返回值: 6
[开始] multiply,参数: (2,), {'b': 3}
[结束] multiply,返回值: 6
3.3 常见错误示例
multiply(a=2, 3) # 错误!关键字参数必须在位置参数之后
报错分析:
SyntaxError: positional argument follows keyword argument
原因:Python语法规定所有位置参数必须出现在关键字参数之前
四、装饰器开发最佳实践
4.1 关键注意事项
- 保留元信息:使用@functools.wraps(func)装饰wrapper
- 多层装饰器:装饰顺序影响执行顺序(从下往上)
- 带参装饰器:需要三层嵌套函数结构
- 性能影响:每次调用都会执行装饰器逻辑
4.2 调试技巧
- 使用
__name__
属性检查函数身份
print(multiply.__name__) # 输出:wrapper(未使用functools时)
五、扩展应用场景
场景类型 | 实现功能 | 示例应用 |
---|---|---|
性能监控 | 统计函数执行时间 | 接口响应时间统计 |
权限校验 | 验证用户访问权限 | 登录状态检查 |
缓存机制 | 缓存函数计算结果 | 斐波那契数列计算 |
异常处理 | 统一异常捕获和日志记录 | 数据库操作重试机制 |
输入验证 | 验证函数参数合法性 | API参数校验 |
# 带缓存功能的装饰器示例
from functools import lru_cache@lru_cache(maxsize=128)
@logger
def factorial(n):return n * factorial(n-1) if n else 1
掌握装饰器技巧,可以显著提升代码的复用性和可维护性。建议读者尝试实现一个支持超时中断的装饰器,这将涉及信号处理和多线程技术,是很好的进阶练习。
@浙大疏锦行