装饰器是以函数或类为参数、并返回替换函数或类的可调用对象。可在不修改原定义的情况下附加行为。
修饰函数
1 | def warp(obj): |
@warp 与 foo = warp(foo) 完全等价:装饰器接收原函数,返回一个新可调用对象并绑定到原名字。
修饰类
1 | def warp(obj): |
自定义装饰器示例:function_timer
标准库装饰器(@property、@lru_cache 等)底层也是「接收 callable、返回 callable」。下面用计时装饰器走通完整写法,帮助理解闭包、参数转发与元数据保留。
无参装饰器
1 | from functools import wraps |
1 | @timefn: work took 0.0xxxx s |
展开等价关系:
1 | work = function_timer(work) |
要点
- 外层
function_timer(fn)接收被装饰函数;内层measure_time是实际替换上去的 wrapper *args, **kwargs保证任意签名都能转发@wraps(fn)必不可少,否则help(work)、mock.patch路径会指向 wrapper
带参装饰器(三层嵌套)
装饰器本身需要配置项时,多一层工厂函数:
1 | def function_timer(threshold=1.0): |
等价于 fast = function_timer(threshold=0.01)(fast):先调用工厂得到 decorator,再装饰目标函数。
写自定义装饰器时注意
- 计 interval 用
time.perf_counter(),不要用易受系统时钟调整影响的time.time() - 装饰异步函数时 wrapper 须为
async def,内部await fn(...) - 生产环境 profiling 用
cProfile/timeit,计时打印适合开发调试
与专篇的关系
本目录各专篇默认读者已理解上述等价关系与自定义写法;具体标准库/第三方装饰器的参数与示例见 00.索引-装饰器速查 与各 {作用对象}-{使用场景}-{名称}.md 专篇。