在 Python 中,带参数的装饰器(decorator with arguments)本质上是三层函数结构

原函数f,装饰器函数g,装饰器外层函数h("INFO"),那么有h("INFO", g(f(X)))

一、为什么需要“带参数”的装饰器

普通装饰器:

def logger(func):
    def wrapper(*args, **kwargs):
        print("函数开始执行")
        result = func(*args, **kwargs)
        print("函数执行结束")
        return result
    return wrapper

使用:

@logger
def add(a, b):
    return a + b

调用:

add(1,2)

输出:

函数开始执行
函数执行结束

但如果我们希望:

@logger(level="INFO")

控制日志等级,就需要带参数装饰器


二、带参数装饰器标准结构

核心结构:三层函数

def decorator(arg1, arg2):
    
    def real_decorator(func):
        
        def wrapper(*args, **kwargs):
            # 扩展逻辑
            result = func(*args, **kwargs)
            return result
        
        return wrapper
    
    return real_decorator

层级关系:

decorator(参数)
        ↓
real_decorator(func)
        ↓
wrapper(*args, **kwargs)

三、完整示例(最经典)

例子:带日志等级

def logger(level):

    def decorator(func):

        def wrapper(*args, **kwargs):
            print(f"[{level}] 函数 {func.__name__} 开始执行")
            result = func(*args, **kwargs)
            print(f"[{level}] 函数 {func.__name__} 执行结束")
            return result

        return wrapper

    return decorator

使用:

@logger("INFO")
def add(a, b):
    return a + b

调用:

add(1,2)

输出:

[INFO] 函数 add 开始执行
[INFO] 函数 add 执行结束

四、执行流程(非常重要)

代码:

@logger("INFO")
def add(a, b):
    return a + b

Python 实际执行:

第一步

执行:

logger("INFO")

返回:

decorator

第二步

执行:

decorator(add)

返回:

wrapper

此时:

add = wrapper

第三步

调用:

add(1,2)

实际上执行:

wrapper(1,2)

内部再调用:

func(1,2)

总结流程:

@logger("INFO")

等价于:

add = logger("INFO")(add)

非常重要!


五、带参数装饰器 + functools.wraps(推荐)

如果不加 wraps,函数信息会丢失:

add.__name__ = wrapper

正确写法:

from functools import wraps

def logger(level):

    def decorator(func):

        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"[{level}] {func.__name__}")
            return func(*args, **kwargs)

        return wrapper

    return decorator

这样:

add.__name__ == "add"

六、经典应用场景(非常重要)

1 日志控制

@log(level="DEBUG")
def train():
    pass

常见于:

  • 深度学习训练
  • API 调用
  • 服务日志

2 权限控制

Web开发中常见:

@require_role("admin")
def delete_user():
    pass

实现:

def require_role(role):

    def decorator(func):

        def wrapper(user, *args, **kwargs):

            if user.role != role:
                raise PermissionError("权限不足")

            return func(user, *args, **kwargs)

        return wrapper

    return decorator

3 重试机制(工程中非常常见)

例如网络请求:

@retry(times=3)
def request_api():
    pass

实现:

def retry(times):

    def decorator(func):

        def wrapper(*args, **kwargs):

            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    print(f"retry {i+1}")

        return wrapper

    return decorator

4 计时器(深度学习训练常用)

@timer(unit="ms")
def train_step():
    pass

实现:

import time

def timer(unit="s"):

    def decorator(func):

        def wrapper(*args, **kwargs):

            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()

            t = end - start
            if unit == "ms":
                t *= 1000

            print(f"time: {t:.3f}{unit}")

            return result

        return wrapper

    return decorator

5 参数检查

@validate_type(int, int)
def add(a, b):
    return a + b

七、进阶:带参数 + 类装饰器

有时也用 class decorator

class Logger:

    def __init__(self, level):
        self.level = level

    def __call__(self, func):

        def wrapper(*args, **kwargs):
            print(self.level)
            return func(*args, **kwargs)

        return wrapper

使用:

@Logger("INFO")

八、工程中最常见的写法总结

标准模板(建议记住):

from functools import wraps

def decorator(arg):

    def real_decorator(func):

        @wraps(func)
        def wrapper(*args, **kwargs):

            # before
            result = func(*args, **kwargs)
            # after

            return result

        return wrapper

    return real_decorator

九、一句话总结

带参数装饰器本质就是:

三层函数结构
参数层 → 装饰器层 → 包装函数层

结构:

decorator(args)
    ↓
decorator(func)
    ↓
wrapper()
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐