Python 装饰器深度解析:从闭包原理到实战应用

Nina Santos | 2026-08-27T20:56:02 | Python

从闭包原理出发,系统讲解 Python 装饰器的工作机制,涵盖带参数装饰器、类装饰器和 functools.wraps 的使用。

# Python 装饰器深度解析 ## 闭包基础 装饰器本质是闭包(Closure)的语法糖。 ```python def outer(msg): def inner(): print('消息: ' + msg) # inner 引用了外层变量 msg return inner hello = outer('你好') hello() # 输出: 消息: 你好 # msg 已经脱离 outer 作用域,但 inner 仍然可以访问它 ``` ## 最简装饰器 ```python def timer(func): import time def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) elapsed = time.time() - start print('{} 耗时: {:.4f}s'.format(func.__name__, elapsed)) return result return wrapper @timer def slow_function(): import time time.sleep(1) return '完成' slow_function() # 输出: slow_function 耗时: 1.0012s ``` `@timer` 等价于 `slow_function = timer(slow_function)` ## functools.wraps 保留元信息 ```python from functools import wraps def timer(func): @wraps(func) # 保留原函数的 __name__, __doc__ 等 def wrapper(*args, **kwargs): import time start = time.time() result = func(*args, **kwargs) print('{} 耗时: {:.4f}s'.format(func.__name__, time.time() - start)) return result return wrapper @timer def process_data(): '''处理数据的函数''' pass print(process_data.__name__) # process_data(不加 wraps 会显示 wrapper) print(process_data.__doc__) # 处理数据的函数 ``` ## 带参数的装饰器 ```python def retry(max_attempts=3, delay=1): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): import time for attempt in range(1, max_attempts + 1): try: return func(*args, **kwargs) except Exception as e: if attempt == max_attempts: raise print('第 {} 次失败: {},{}s 后重试'.format( attempt, str(e), delay)) time.sleep(delay) return wrapper return decorator @retry(max_attempts=5, delay=2) def call_api(): import random if random.random() retry -> log_params -> fetch_data ``` ## 实战:权限校验装饰器 ```python def require_role(*roles): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # 从请求上下文获取当前用户 user = get_current_user() if user.role not in roles: raise PermissionError( '需要角色: {},当前角色: {}'.format(roles, user.role)) return func(*args, **kwargs) return wrapper return decorator @require_role('admin', 'manager') def delete_user(user_id): pass ``` ## 注意事项 1. 始终使用 `@wraps` 保留原函数元信息 2. 装饰器执行顺序是从下到上 3. 注意闭包变量的引用问题(循环中的陷阱) 4. 异步函数需要用 `async` 装饰器

← Back to Blog