Python多层装饰器案例如何叠加装饰

wen python案例 30

Python多层装饰器实战指南:从单层叠加到复杂场景

📚 目录导读

  1. 装饰器基础回顾:核心概念与单层用法
  2. 两层装饰器叠加:执行顺序与典型应用
  3. 多层装饰器叠加:真相揭示与设计陷阱
  4. 实战案例:日志 + 权限 + 缓存的三层叠加
  5. 常见误区问答:为什么我的装饰器不生效?

装饰器基础回顾:核心概念与单层用法

装饰器本质是一个接受函数作为参数并返回新函数的可调用对象(通常用语法糖),单层装饰器的核心价值在于不修改原函数代码即可增强功能,

Python多层装饰器案例如何叠加装饰

def log_time(func):
    def wrapper(*args, **kwargs):
        print(f"开始执行:{func.__name__}")
        result = func(*args, **kwargs)
        print(f"结束执行:{func.__name__}")
        return result
    return wrapper
@log_time
def greet(name):
    return f"你好,{name}"

关键特性:使用functools.wraps保留原函数的元数据(__name____doc__等),否则叠加装饰器后会导致函数信息丢失。

两层装饰器叠加:执行顺序与典型应用

当我们需要添加多个独立功能时(如日志+性能监控),两层装饰器叠加的使用场景最为普遍。执行顺序遵循“从上到下包裹,从下到上执行”的规则

def add_log(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] 调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
def add_timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"[TIMER] {func.__name__} 耗时: {time.time()-start:.3f}秒")
        return result
    return wrapper
@add_log          # 外层装饰器
@add_timer         # 内层装饰器
def fetch_data():
    import time
    time.sleep(0.2)
    return "数据"

执行后输出顺序

  1. [LOG] 调用函数: fetch_data(外层add_logwrapper先执行)
  2. [TIMER] fetch_data 耗时: 0.202秒(内层add_timerwrapper在内部执行)

逻辑拆解@add_log等价于fetch_data = add_log(add_timer(fetch_data)),因此add_logwrapper是外层函数,它会在调用内部add_timerwrapper前后插入日志代码,而add_timerwrapper又包裹了原函数。

多层装饰器叠加:真相揭示与设计陷阱

三层或以上装饰器叠加在真实项目中并不罕见(认证→缓存→日志→重试),但开发者常会陷入以下陷阱:

陷阱1:元数据污染
未使用@functools.wraps时,每层包装都会继承上一层的名称,解决方案:

from functools import wraps
def retry(max_tries=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(max_tries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if i == max_tries - 1:
                        raise
            return None
        return wrapper
    return decorator

陷阱2:参数传递错误
当装饰器需要传递参数(如@decorator(arg)),必须使用三层函数结构(外层接收参数,中层接收函数,内层接收实际参数)。

陷阱3:执行顺序误解
测试以下三层叠加的调用栈:

@decorator3
@decorator2
@decorator1
def target(): pass

实际调用顺序:decorator3(decorator2(decorator1(target)))
执行流程:decorator3的wrapperdecorator2的wrapperdecorator1的wrappertarget→返回

实战案例:日志 + 权限 + 缓存的三层叠加

构建一个模拟电商系统的API接口增强方案:

import functools
import time
from datetime import datetime
# 1. 日志装饰器(记录调用时间)
def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[{datetime.now()}] {func.__name__} 被调用")
        return func(*args, **kwargs)
    return wrapper
# 2. 权限验证装饰器(模拟角色校验)
def require_role(role):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 假设user_role通过全局变量或请求上下文获取
            user_role = "admin"  # 模拟
            if user_role != role:
                raise PermissionError(f"需要{role}权限,当前用户角色: {user_role}")
            return func(*args, **kwargs)
        return wrapper
    return decorator
# 3. 缓存装饰器(简单内存缓存)
def cache_result(expire_seconds=60):
    def decorator(func):
        cache = {}
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = str(args) + str(kwargs)
            now = time.time()
            if key in cache and (now - cache[key]['time'] < expire_seconds):
                print("[缓存命中]")
                return cache[key]['data']
            result = func(*args, **kwargs)
            cache[key] = {'data': result, 'time': now}
            return result
        return wrapper
    return decorator
# 叠加使用:权限最外层 → 缓存中间层 → 日志最内层
@require_role("admin")      # 第一层包裹(最外层)
@cache_result(expire_seconds=30)  # 第二层包裹
@log_call                   # 第三层包裹(最内层)
def get_user_order(user_id):
    """获取用户订单信息(模拟耗时)"""
    time.sleep(0.1)
    return {"user_id": user_id, "orders": ["orderA", "orderB"]}
# 测试调用
print(get_user_order(1001))

执行流程分析

  1. 外层验证权限,若失败直接抛错
  2. 通过后进入缓存层,判断是否过期
  3. 最终进入日志层记录调用时间,随后执行原函数

设计建议:将静态检查类装饰器(权限、限流)放在外层,动态处理类装饰器(缓存、日志)放在内层,以优化性能。

常见误区问答:为什么我的装饰器不生效?

问题1:装饰器没有返回wrapper函数
❌ 错误示例:

def bad_decorator(func):
    print("装饰器被调用了")  # 只执行一次

✅ 正确:必须返回一个函数对象。

问题2:多层装饰器丢失原函数参数
当使用*args, **kwargs未正确传递时,会导致参数缺失,确保每一层都正确转发参数。

问题3:带参数的装饰器嵌套层数不对
❌ 错误示例:

@decorator_with_arg(arg)  # 实际上是两次调用
def func(): pass

需保持三层结构:decorator(arg)(func)

问题4:@wraps未被使用导致的调试困难
打印func.__name__会得到wrapper而非原函数名,解决方案:

from functools import wraps
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

通过本文的案例与解析,你应该已掌握多层装饰器的叠加机制:执行顺序与包裹顺序相反,每一层都需保留函数的签名与元数据,在实际项目中,建议将复杂度控制在3层以内,并为每个装饰器提供清晰的文档说明其职责与执行阶段。

抱歉,评论功能暂时关闭!