Python装饰器详解

发布于:2025-08-16 ⋅ 阅读:(27) ⋅ 点赞:(0)

Python装饰器详解

装饰器(Decorator)是Python中一个非常强大且有用的特性,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。下面我将详细解释装饰器的使用和运行过程。

一、装饰器基础概念

装饰器本质上是一个Python函数(或其他可调用对象),它可以让其他函数在不做任何代码变动的情况下增加额外功能(增加新功能的函数偷偷换掉被装饰的函数)。装饰器的返回值也是一个函数对象(返回的对象就是那个增加新功能的函数)。

二、装饰器的运行过程

装饰器的运行过程分为两个阶段,装饰阶段和调用阶段

1. 装饰阶段(Definition Time)模块加载时执行

当Python解释器遇到 @decorator 语法时,会立即执行装饰器逻辑(在函数定义时执行,而非调用时)。
装饰器函数返回一个函数(增加新功能的函数)
将新函数赋值给被装饰器修饰的函数(原函数)

2. 调用阶段(Call Time)显式调用时执行

当调用被装饰的函数时,实际执行的是装饰器函数返回的函数(函数名字虽然一样 但是函数内容已经是装饰器修饰刚改过的了)。

当 Python 解释器遇到 @decorator 语法时,会立即执行装饰器逻辑(在函数/类定义阶段执行),这一行为是装饰器的核心特性。下面通过分步拆解和示例详细说明:


关键概念解析

  1. 定义阶段(Definition Time)

    • 当 Python 加载模块执行代码块时,遇到 @decorator 会立即执行装饰器函数。
    • 此时不会执行被装饰的函数,而是将函数/类作为参数传递给装饰器。
  2. 调用阶段(Call Time)

    • 当通过函数名显式调用被装饰的函数时,才会执行装饰器返回的包装逻辑和原函数逻辑。

执行流程分步说明

场景代码
def decorator(func):
    print("装饰器执行:修改函数行为")  # 此代码在定义阶段立即执行
    def wrapper(*args, **kwargs):
        print("调用阶段:装饰器添加的逻辑")
        return func(*args, **kwargs)
    return wrapper

@decorator  # 解释器遇到此行时立即执行 decorator(example)
def example():
    print("原函数逻辑")

print("------ 分割线 ------")
example()  # 第一次调用
example()  # 第二次调用
输出结果
装饰器执行:修改函数行为    # 定义阶段输出
------ 分割线 ------       # 模块加载完成后
调用阶段:装饰器添加的逻辑   # 第一次调用输出
原函数逻辑
调用阶段:装饰器添加的逻辑    # 第二次调用输出
原函数逻辑

具体执行步骤

  1. 定义阶段(模块加载时)

    • Python 解释器遇到 @decorator 时,会立即调用 decorator(example),其中 example 是尚未执行的原始函数对象。
    • 执行 decorator 函数体:
      • 打印 "装饰器执行:修改函数行为"
      • 定义 wrapper 函数(但不会执行它)
      • 返回 wrapper 函数
    • 关键操作:将 example 变量重新绑定到 wrapper 函数(原函数可通过 example.__wrapped__ 访问)。
  2. 调用阶段(显式调用时)

    • 当代码执行 example() 时:
      • 实际调用的是 wrapper(*args, **kwargs)
      • 执行 wrapper 内部的逻辑(如打印 "调用阶段:装饰器添加的逻辑"
      • 通过 func(*args, **kwargs) 调用原始 example 函数

类比说明

阶段 类比场景 发生时机
定义阶段 给手机安装滤镜APP(@decorator) 安装APP时(模块加载/代码执行)
调用阶段 用滤镜APP拍照(调用被装饰函数) 按下快门时(显式函数调用)

常见误区澄清

  1. 误区:认为装饰器逻辑在函数调用时才执行。
    正解:装饰器本身(即 decorator(func) 的调用)在定义阶段立即执行,返回的 wrapper 逻辑在调用阶段执行。

  2. 误区:每次调用被装饰函数都会重新执行装饰器。
    正解:装饰器仅在定义阶段执行一次,后续调用直接使用已生成的 wrapper


验证实验

通过修改示例观察执行顺序:

print("模块开始加载")

def decorator(func):
    print("装饰器运行时间点")  # 观察何时打印
    return func  # 直接返回原函数(无包装)

@decorator
def test():
    pass

print("模块加载完成")
test()  # 调用函数

输出

模块开始加载
装饰器运行时间点  # 在模块加载过程中执行
模块加载完成

(若装饰器在调用时执行,此处的 装饰器运行时间点 会在 test() 调用后打印)


三、使用示例

以下是 Python 中各类装饰器的 定义说明参数/返回值详解具体使用示例


一、函数装饰器(无参数)

定义说明
  • 作用:修改或增强函数行为
  • 入参func(被装饰的函数对象)
  • 返回值wrapper 函数(替换原函数)
示例
def log_time(func):
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)  # 调用原函数
        print(f"耗时: {time.time() - start:.2f}s")
        return result
    return wrapper

@log_time  # 相当于 func = log_time(func)
def calculate(n):
    return sum(i*i for i in range(n))

print(calculate(1000000))  # 调用时自动计时

二、带参数的函数装饰器

定义说明
  • 作用:根据参数动态生成装饰器
  • 入参:装饰器参数 → 返回实际装饰器 → 接收 func
  • 返回值:多层嵌套的 wrapper 函数
示例
def repeat(times):  # 装饰器工厂
    def decorator(func):  # 实际装饰器
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)  # 传递参数
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")  # 打印3次问候语

三、类装饰器(装饰函数)

定义说明
  • 作用:用类实现装饰器逻辑
  • 入参func 通过 __init__ 接收
  • 返回值:实现 __call__ 方法使实例可调用
示例
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.calls = 0

    def __call__(self, *args, **kwargs):
        self.calls += 1
        print(f"调用次数: {self.calls}")
        return self.func(*args, **kwargs)

@CountCalls  # 相当于 func = CountCalls(func)
def say_hello():
    print("Hello!")

say_hello()  # 每次调用统计次数
say_hello()

四、类装饰器(装饰类)

定义说明
  • 作用:动态修改类定义
  • 入参cls(被装饰的类)
  • 返回值:修改后的类
示例
def add_method(cls):
    def decorator(func):
        setattr(cls, func.__name__, func)
        return func
    return decorator

@add_method  # 装饰类
class MyClass:
    pass

# 动态添加类方法
@MyClass.add_method
def new_method(self):
    print("动态添加的方法")

obj = MyClass()
obj.new_method()  # 调用新增方法

五、保留元信息的装饰器

定义说明
  • 作用:避免装饰器掩盖原函数的 __name____doc__ 等属性
  • 关键:使用 functools.wraps 复制元信息
示例
from functools import wraps

def preserve_meta(func):
    @wraps(func)  # 将原函数的元信息复制到wrapper
    def wrapper(*args, **kwargs):
        """Wrapper函数的docstring"""
        return func(*args, **kwargs)
    return wrapper

@preserve_meta
def original():
    """原始函数的docstring"""
    pass

print(original.__name__)  # 输出 "original"(而非wrapper)
print(original.__doc__)   # 输出 "原始函数的docstring"

六、方法装饰器(装饰类方法)

定义说明
  • 作用:装饰类中的实例方法
  • 注意wrapper 需接收 self 参数
示例
def validate_input(func):
    def wrapper(self, x):
        if not isinstance(x, int):
            raise ValueError("输入必须是整数")
        return func(self, x)
    return wrapper

class Calculator:
    @validate_input
    def square(self, x):
        return x * x

calc = Calculator()
print(calc.square(5))  # 正常调用
calc.square("abc")     # 触发验证错误

七、异步函数装饰器

定义说明
  • 作用:装饰协程函数(async def
  • 关键wrapper 需定义为 asyncawait 原函数
示例
def async_retry(max_attempts):
    def decorator(func):
        async def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts+1):
                try:
                    return await func(*args, **kwargs)
                except Exception as e:
                    print(f"尝试 {attempt} 失败: {e}")
            raise RuntimeError("所有重试均失败")
        return wrapper
    return decorator

@async_retry(max_attempts=3)
async def fetch_data():
    # 模拟可能失败的异步操作
    raise ConnectionError("网络错误")

import asyncio
asyncio.run(fetch_data())

八、属性装饰器(@property

定义说明
  • 作用:将方法转为属性调用
  • 相关装饰器@property, @x.setter, @x.deleter
示例
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """Getter: 获取半径"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """Setter: 设置半径"""
        if value <= 0:
            raise ValueError("半径必须为正数")
        self._radius = value

circle = Circle(5)
print(circle.radius)  # 调用getter(无需括号)
circle.radius = 10    # 调用setter

总结对比表

类型 定义形式 核心参数 返回值要求 典型场景
函数装饰器 def decorator(func): func wrapper 函数 日志、计时、验证
带参装饰器 def decorator(params): → 嵌套 参数 + func 多层 wrapper 动态配置装饰行为
类装饰器(函数) class Decorator: + __call__ func 可调用实例 状态保持(如计数器)
类装饰器(类) def decorator(cls): cls 修改后的类 动态添加类成员
异步装饰器 async def wrapper: + await func() 协程函数 异步包装函数 重试、异步日志
属性装饰器 @property + @x.setter 描述符对象 属性访问控制

通过组合这些模式,可以实现复杂的装饰器逻辑,同时保持代码的可读性。

以下是针对每一类装饰器的 实际应用场景使用建议,结合具体需求说明其适用性和最佳实践:


四、应用场景及示例

一、函数装饰器(无参数)

应用场景
  1. 性能监控:记录函数执行时间(如 API 接口耗时)
  2. 调试工具:打印函数输入/输出(调试复杂逻辑时)
  3. 权限校验:检查用户权限(如 @login_required
建议
  • 适用于 无状态 的简单增强逻辑
  • 优先使用 functools.wraps 保留元信息
  • 示例扩展:数据库事务自动提交
    def db_transaction(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
                db.commit()  # 成功则提交
                return result
            except Exception as e:
                db.rollback()  # 失败回滚
                raise e
        return wrapper
    
  • 示例扩展:函数执行时间计算
import time
from functools import wraps

# 定义装饰器:计算函数执行时间
def log_time(func):
    @wraps(func)  # 保留原函数元信息
    def wrapper(*args, **kwargs):
        start = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 调用原函数
        end = time.time()  # 记录结束时间
        print(f"{func.__name__} 耗时: {end - start:.4f}秒")  # 打印耗时
        return result  # 返回原函数结果
    return wrapper  # 返回包装后的函数

# 使用装饰器
@log_time
def calculate_sum(n):
    """计算1到n的平方和"""
    return sum(i*i for i in range(n))

# 测试
print(calculate_sum(1000000))  # 输出结果和耗时
执行输出:
calculate_sum 耗时: 0.0453秒
333332833333500000

二、带参数的函数装饰器

应用场景
  1. 重试机制:根据参数控制重试次数和间隔(如网络请求)
  2. 缓存控制:动态设置缓存过期时间(如 @cache(ttl=300)
  3. 权限分级:通过参数指定所需权限等级
建议
  • 需要 动态配置 时使用
  • 避免嵌套过深(超过 3 层可考虑用类装饰器替代)
  • 示例扩展:指数退避重试
    def retry(max_attempts, delay=1):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                for attempt in range(1, max_attempts+1):
                    try:
                        return func(*args, **kwargs)
                    except Exception:
                        if attempt == max_attempts:
                            raise
                        time.sleep(delay * 2 ** attempt)  # 指数退避
            return wrapper
        return decorator
    

三、类装饰器(装饰函数)

应用场景
  1. 状态维护:统计函数调用次数/频率
  2. 资源管理:自动管理文件/锁的开关(如 with 语句的替代)
  3. 单例模式:强制一个类只有一个实例
建议
  • 需要 长期保持状态 时使用
  • 示例扩展:函数调用限流
    class RateLimiter:
        def __init__(self, func, max_calls=10):
            self.func = func
            self.max_calls = max_calls
            self.calls = 0
    
        def __call__(self, *args, **kwargs):
            if self.calls >= self.max_calls:
                raise RuntimeError("超过调用限额")
            self.calls += 1
            return self.func(*args, **kwargs)
    

四、类装饰器(装饰类)

应用场景
  1. 动态注入:为类添加通用方法(如 to_dict()
  2. 接口适配:统一实现某些接口(如序列化协议)
  3. 注册机制:自动注册子类(如插件系统)
建议
  • 适用于 框架开发元编程
  • 示例扩展:自动注册子类
    registered_plugins = {}
    
    def register_plugin(name):
        def decorator(cls):
            registered_plugins[name] = cls
            return cls
        return decorator
    
    @register_plugin("csv_reader")
    class CSVReader:
        pass
    

五、保留元信息的装饰器

应用场景
  1. 文档生成:确保 help()__doc__ 正确显示
  2. 调试工具:保留原始函数名便于日志追踪
  3. AOP 框架:面向切面编程时保持堆栈信息
建议
  • 始终 对函数装饰器使用 @wraps
  • 调试时可通过 __wrapped__ 访问原函数

六、方法装饰器(装饰类方法)

应用场景
  1. 输入验证:校验方法参数类型/范围
  2. 单位转换:自动转换参数单位(如角度 ↔ 弧度)
  3. 上下文管理:自动加锁/释放资源
建议
  • 注意 self 是类实例,可访问类属性
  • 示例扩展:线程锁保护
    def thread_safe(method):
        @wraps(method)
        def wrapper(self, *args, **kwargs):
            with self._lock:  # 假设类中有 _lock 属性
                return method(self, *args, **kwargs)
        return wrapper
    

七、异步函数装饰器

应用场景
  1. 重试机制:异步请求失败后自动重试
  2. 超时控制:为协程添加超时限制
  3. 缓存异步结果:避免重复计算
建议
  • 确保 wrapper 是异步函数
  • 示例扩展:异步缓存
    def async_cache(func):
        cache = {}
        @wraps(func)
        async def wrapper(*args):
            if args not in cache:
                cache[args] = await func(*args)
            return cache[args]
        return wrapper
    

八、属性装饰器(@property

应用场景
  1. 数据校验:设置属性时检查合法性
  2. 惰性计算:延迟计算复杂属性
  3. 兼容性封装:将旧接口封装为属性
建议
  • 避免在 @property 中执行耗时操作
  • 示例扩展:惰性加载
    class DataLoader:
        @property
        def data(self):
            if not hasattr(self, '_cached_data'):
                self._cached_data = self._load_data()  # 耗时操作
            return self._cached_data
    

通用建议

  1. 保持简单:装饰器应专注于单一功能
  2. 文档齐全:明确说明装饰器的作用和参数
  3. 性能考量:避免在装饰器中引入显著开销(如频繁 IO)
  4. 测试覆盖:特别注意装饰器对原函数签名的影响

通过合理选择装饰器类型,可以显著提升代码的可复用性和可维护性。

九、附录闭包(Closure)的详细解释

闭包是Python中一个非常重要的概念,特别是在装饰器的实现中起着关键作用。让我们用这个装饰器例子来彻底理解闭包:

示例代码
def repeat(num_times):          # 外层函数
    def decorator(func):        # 中层函数
        def wrapper(*args, **kwargs):  # 内层函数
            for _ in range(num_times):  # 关键点:这里使用了num_times
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

一、什么是闭包?

闭包是指 一个函数能够记住并访问其词法作用域(定义时的作用域)中的变量,即使该函数在其词法作用域之外执行。

在装饰器中的体现:

  1. 外层变量num_times(装饰器参数)
  2. 内层函数wrapper(最终被调用的函数)
  3. 闭包关系wrapper 可以访问 num_times,即使 repeat() 函数已经执行完毕

闭包在装饰器中的具体表现

1. 当执行 @repeat(3) 时:
decorator_func = repeat(3)  # num_times=3 被保存在闭包中
  • 此时 num_times=3 已经与返回的 decorator 函数绑定
2. 当装饰函数时:
say_hello = decorator_func(say_hello)  # 返回wrapper
  • 新的闭包形成:wrapper 可以访问:
    • func(原函数 say_hello
    • num_times=3(来自外层)
3. 闭包验证:
print(say_hello.__closure__[0].cell_contents)  # 输出:3
print(say_hello.__closure__[1].cell_contents)  # 输出:<function say_hello at 0x...>

闭包的三要素

  1. 嵌套函数:至少两层函数嵌套(本例是三层)
  2. 内部函数引用外部变量wrapper 引用了 num_times
  3. 外部函数返回内部函数repeat() 返回 decoratordecorator() 返回 wrapper

为什么需要闭包?

  1. 保持状态:在多次调用中记住装饰器参数(num_times=3
  2. 实现封装:隐藏变量(num_times 对调用者不可见)
  3. 延迟绑定:参数在装饰时确定,但实际使用时才生效

闭包的内存管理

@repeat(3)
def say_hello(): pass

@repeat(5)
def say_hi(): pass
  • 两个装饰器实例有独立的闭包环境:
    • say_hello 的闭包保存 num_times=3
    • say_hi 的闭包保存 num_times=5

闭包的实际应用场景

  1. 装饰器(如本例)
  2. 回调函数(保持上下文)
  3. 延迟计算
  4. 函数工厂(生成不同配置的函数)

与普通变量的区别

非闭包情况:

def bad_repeat(func):
    num_times = 3  # 局部变量
    def wrapper():
        for _ in range(num_times):  # 每次调用都重新读取
            func()
    return wrapper

问题:无法动态指定 num_times

闭包的优势:

def good_repeat(num_times):  # 闭包保存这个值
    def decorator(func):
        def wrapper():
            for _ in range(num_times):  # 从闭包读取
                func()
        return wrapper
    return decorator

闭包的底层原理

Python通过 __closure__ 属性实现闭包:

# 查看闭包内容
print(say_hello.__closure__)
# 输出:(<cell at 0x...: int object at 0x...>, 
#       <cell at 0x...: function object at 0x...>)

每个 cell 对象保存一个被引用的外部变量。

二、 Python装饰器的完整执行过程

# 定义一个带参数的装饰器
def repeat(num_times):
    # 这是一个装饰器工厂函数,它返回实际的装饰器
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator
# 使用带参数的装饰器
@repeat(num_times=3)
def say_hello():
    print("Hello!")
say_hello()

输出

Hello!
Hello!
Hello!
print(say_hello.__closure__[0].cell_contents)  # 输出:3
print(say_hello.__closure__[1].cell_contents)  # 输出:<function say_hello at 0x...>

输出

<function say_hello at 0x000001F69706E560>
3
1. 装饰阶段(Definition Time)

当Python解释器遇到 @decorator 语法时,会在函数定义时立即执行以下步骤:

① 解析装饰器语法

@repeat(num_times=3)  # 解释器先执行 repeat(3),再装饰 say_hello
def say_hello(): ...
  • 等价于 say_hello = repeat(3)(say_hello)

② 执行装饰器工厂

repeat(3)  # 调用外层函数
  • 接收参数 num_times=3
  • 返回内层的 decorator 函数
  • 形成闭包num_times=3 被保存在 decorator 的环境中

③ 执行实际装饰器

decorator(say_hello)  # 用返回的 decorator 处理 say_hello
  • 接收原函数 say_hello 作为参数
  • 定义 wrapper 函数,并通过闭包绑定:
    • num_times=3(来自外层)
    • func=say_hello(原函数)
  • 返回 wrapper 函数

④ 完成函数替换

say_hello = wrapper  # 原函数被替换为 wrapper
  • 此时 say_hello__name__ 变为 "wrapper"(可用 @functools.wraps 修复)

2. 调用阶段(Call Time)

当调用被装饰的函数时,实际执行的是替换后的 wrapper 函数:

① 调用入口

say_hello()  # 实际调用的是 wrapper()

② 执行包装逻辑

def wrapper(*args, **kwargs):
    # 从闭包中读取 num_times=3
    for _ in range(3):
        # 通过闭包调用原函数 say_hello()
        result = func(*args, **kwargs)  
    return result
  1. 从闭包获取 num_times=3
  2. 循环3次调用原函数 func()(即最初的 say_hello
  3. 返回最后一次调用的结果

③ 执行原函数

def say_hello():  # 原始函数逻辑
    print("Hello!")
  • 每次循环都会执行此代码
  • 若无返回值,默认返回 None

关键点总结

  1. 装饰阶段(函数定义时):

    • 解析 @ 语法 → 执行装饰器工厂 → 生成闭包 → 替换原函数
  2. 调用阶段(函数调用时):

    • 执行 wrapper → 通过闭包访问参数和原函数 → 插入额外逻辑 → 调用原函数
  3. 闭包是核心机制

    • 同时保存了装饰器参数 (num_times) 和原函数 (func)
    • 使得装饰器可以"记忆"配置信息
  4. 函数替换是实现基础

    • 原函数被 wrapper 替换,但 wrapper 通过闭包仍能调用原函数

这种设计实现了 声明式编程——只需添加 @decorator 即可修改函数行为,而无需侵入原函数代码。

@wraps 是 Python 中 functools 模块提供的一个装饰器,专门用于保留被装饰函数的原始元信息(如函数名、文档字符串、参数列表等)。它的核心作用是解决装饰器的一个常见问题:直接使用装饰器会导致原函数的元信息被替换为装饰器内部 wrapper 函数的元信息。


三、为什么需要 @wraps

当你不使用 @wraps 时:

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        """wrapper函数的docstring"""
        return func(*args, **kwargs)
    return wrapper

@simple_decorator
def original():
    """原始函数的docstring"""
    pass

print(original.__name__)  # 输出:'wrapper'(期望是 'original')
print(original.__doc__)   # 输出:'wrapper函数的docstring'(期望是原始文档)

此时,原函数 original 的元信息被 wrapper 覆盖了。


使用 @wraps 的效果

from functools import wraps

def correct_decorator(func):
    @wraps(func)  # 关键点:将原函数的元信息复制到wrapper
    def wrapper(*args, **kwargs):
        """wrapper函数的docstring"""
        return func(*args, **kwargs)
    return wrapper

@correct_decorator
def original():
    """原始函数的docstring"""
    pass

print(original.__name__)  # 输出:'original'(正确保留)
print(original.__doc__)   # 输出:'原始函数的docstring'(正确保留)

@wraps 的工作原理

  1. 复制元信息
    将原函数 func 的以下属性复制到 wrapper 函数中:

    • __name__(函数名)
    • __doc__(文档字符串)
    • __module__(所属模块)
    • __annotations__(类型注解)
    • __dict__(其他自定义属性)
  2. 隐藏包装痕迹
    通过 functools.WRAPPER_ASSIGNMENTSfunctools.WRAPPER_UPDATES 两个常量控制要复制的属性列表。


实际应用场景

  1. 调试和日志
    保留真实函数名便于日志追踪。

    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")  # 正确显示原函数名
        return func(*args, **kwargs)
    
  2. API 文档生成
    确保 help() 或 Sphinx 生成的文档显示原函数的文档字符串。

  3. 依赖函数签名的框架
    如 Flask 的路由装饰器、FastAPI 的依赖注入等需要读取函数元信息。


注意事项

  • 必须嵌套在装饰器最内层
    @wraps(func) 应直接修饰 wrapper 函数。

    def decorator(func):
        @wraps(func)  # 正确位置
        def wrapper(*args, **kwargs):
            pass
        return wrapper
    
  • 不会自动处理参数签名
    如需完整保留参数签名(用于 inspect.signature),需额外使用 functools.update_wrapper 或第三方库(如 decorator 库)。


完整示例

from functools import wraps
import inspect

def debug_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调试: 调用 {func.__name__},参数: {args}, {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@debug_decorator
def add(a: int, b: int) -> int:
    """将两个数字相加"""
    return a + b

# 测试元信息保留
print(add.__name__)            # 输出: 'add'
print(add.__doc__)             # 输出: '将两个数字相加'
print(inspect.signature(add))  # 输出: (a: int, b: int) -> int

输出结果:

调试: 调用 add,参数: (2, 3), {}
5
add
将两个数字相加
(a: int, b: int) -> int

总结:@wraps 是装饰器开发中的最佳实践,它能避免因装饰器引入的元信息丢失问题,确保代码的调试、文档和反射功能正常工作。


网站公告

今日签到

点亮在社区的每一天
去签到