Python:使用functiontools.wraps来实现装饰器。

你这里的 warps 实际上是 wrapsfunctools.wraps),我下面按官方 API + 实战角度完整给你讲清楚。



一、wraps 是什么?

functools.wraps(wrapped,
                assigned=WRAPPER_ASSIGNMENTS,
                updated=WRAPPER_UPDATES)

📌 wraps 是一个“装饰器的装饰器”

  • 用来装饰 wrapper 函数
  • 目的是:复制被装饰函数(wrapped)的元数据到 wrapper 上


二、wraps 的完整 API

函数签名

functools.wraps(
    wrapped,
    assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'),
    updated=('__dict__',)
)


三、参数详解(非常重要)

1️⃣ wrapped(必填)

wrapped: Callable
  • 被装饰的原函数
  • wraps 会把它的属性复制到 wrapper
@wraps(original_func)
def wrapper(...):
    ...


2️⃣ assigned(可选)

assigned: tuple[str]

📌 直接赋值复制的属性名

默认值:

('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')

含义说明:

属性作用
__name__函数名
__qualname__完整限定名
__doc__文档字符串
__module__所属模块
__annotations__类型注解

📌 示例:只复制 name 和 doc

@wraps(func, assigned=("__name__", "__doc__"))
def wrapper():
    ...


3️⃣ updated(可选)

updated: tuple[str]

📌 用于 update(合并)的属性

默认值:

('__dict__',)

__dict__ 是什么?

  • 存储函数的自定义属性
def f(): pass
f.version = "1.0"

行为区别:

属性复制方式
assigned覆盖赋值
updated调用 .update() 合并


四、wraps 的返回值

wraps(...) -> Callable

⚠️ wraps 返回的是一个装饰器函数

decorator = wraps(func)
decorator(wrapper) -> wrapper

等价于:

wrapper = update_wrapper(wrapper, func)


五、wraps 与 update_wrapper 的关系

实际源码关系

def wraps(wrapped, assigned=..., updated=...):
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

👉 wraps 是 update_wrapper 的语法糖



update_wrapper(底层函数)

functools.update_wrapper(
    wrapper,
    wrapped,
    assigned=WRAPPER_ASSIGNMENTS,
    updated=WRAPPER_UPDATES
)

📌 如果你需要动态包装,直接用它



六、wraps 做了哪些“隐藏操作”?

除了复制属性,还会设置:

wrapper.__wrapped__ = wrapped

这个属性的作用:

  • 支持 inspect.unwrap
  • 支持多层装饰器拆解
  • 框架反射、调试工具依赖


七、完整示例(带所有参数)

from functools import wraps

def decorator(func):
    @wraps(
        func,
        assigned=("__name__", "__doc__", "__annotations__"),
        updated=("__dict__",)
    )
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


八、什么时候需要自定义 assigned / updated?

场景 1:避免覆盖 wrapper 自己的 doc

@wraps(func, assigned=("__name__",))

场景 2:多层代理函数

@wraps(func, updated=())


九、常见误区

@wraps 装饰原函数
装饰 wrapper

@wraps() 不传 wrapped
✔ 必须传原函数


十、测试代码

from functools import wraps


def test_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

def test_decorator_1(func):
    def wrapper(*args, **kwargs):
        print("Start")
        result = func(*args, **kwargs)
        print("End")
        return result
    return wrapper

@test_decorator
def function1(n):
    return n * n

@test_decorator_1
def function2(n):
    return n + n




if __name__ == "__main__":
    print(function1.__name__)
    print(function1.__qualname__)
    print(function1.__doc__)
    print(function1.__module__)
    print(function1.__annotations__)
    print("-----")
    print(function2.__name__)
    print(function2.__qualname__)
    print(function2.__doc__)
    print(function2.__module__)
    print(function2.__annotations__)

十一、面试级总结

functools.wraps 是用于装饰装饰器内部 wrapper 函数的工具,通过复制被装饰函数的元数据并设置 __wrapped__,保证函数身份、调试、反射和框架行为的正确性。
文章标签:

评论(0)