装饰器执行的基本原理
Python装饰器在程序运行过程中遵循独特的执行逻辑,其核心特性体现在模块加载阶段的即时执行。通过示例7-2的registration.py 模块,我们可以清晰观察到装饰器与函数执行的时序差异。
registry = []def register(func):print('running register(%s)' % func)registry.append(func) return func @register
def f1(): print('running f1()')@register
def f2(): print('running f2()')def f3(): print('running f3()')def main():print('running main()')print('registry ->', registry)f1(); f2(); f3()if __name__ == '__main__': main()
关键执行时序分析
模块加载阶段
当模块被导入(import registration)或作为脚本运行时,装饰器会立即执行
输出显示装饰器注册过程发生在main函数之前:
running register(<function f1 at 0x100631bf8>)
running register(<function f2 at 0x100631c80>)
函数执行阶段
被装饰函数仅在显式调用时执行
main函数中调用f1/f2时才会输出:
running f1()
running f2()
running f3()
装饰器与函数的执行对比
执行阶段 | 装饰器行为 | 函数行为 |
---|---|---|
模块加载时 | 立即执行(注册函数) | 仅保存函数定义 |
显式调用时 | 无额外操作 | 执行函数体 |
实际应用中的注意事项
模块组织建议
装饰器应独立模块定义(如utils/decorators.py )
示例中的同模块定义仅用于演示
装饰器返回值规范
通常应返回新包装函数:
def logged(func): @wraps(func) def wrapper(*args, **kwargs): print("Calling", func.name) return func(*args, **kwargs) return wrapper
Web框架中的典型应用
路由注册模式
routes = []def route(url):def decorator(func):routes.append((url, func))return func return decorator @route("/home")
def home(): ...
性能监控装饰器
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):start = time.time() result = func(*args, **kwargs)print(func.name, "took", time.time()-start, "s") return result return wrapper
最佳实践总结
时序控制
利用模块加载时的装饰器执行特性实现自动注册
通过延迟执行保证函数调用的灵活性
设计模式
结合functools.wraps 保持函数元数据
使用闭包实现状态保持(如计数器装饰器)
调试技巧
通过打印装饰器执行日志定位初始化问题
使用pdb.set_trace() 在装饰器内部设置断点
这种模块加载时的即时执行特性,使得装饰器成为Python实现"元编程"的强大工具。理解其执行时序不仅有助于编写高效代码,更能帮助开发者设计出符合Pythonic规范的模块化系统。