Skip to main content
Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Functools -- update_wrapper

✅ 1. functools.update_wrapper 是什么?

update_wrapper 是用于“把一个函数的元数据复制到另一个函数”的工具。

就是 wraps() 的底层实现工具。

装饰器写法:

from functools import wraps

其实等价于:

from functools import update_wrapper

wraps 不过是 update_wrapper 的语法糖。

📌 2. 最简单的例子

from functools import update_wrapper

def deco(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    
    update_wrapper(wrapper, func)
    return wrapper

@deco
def add(a, b):
    """加法函数"""
    return a + b

print(add.__name__)  # add
print(add.__doc__)   # 加法函数

如果不使用 update_wrapper,结果会是:

wrapper
None

🟦 3. update_wrapper 的函数签名

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

含义:

参数 含义
wrapper 你的装饰器内部的函数(被修改的对象)
wrapped 原始函数(被装饰的函数)
assigned 需要复制的属性列表(默认有 name, doc…)
updated 需要“更新而不是覆盖”的属性(例如 wrapper.dict)

📌 4. 默认复制哪些字段?

默认的:

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

WRAPPER_UPDATES = ('__dict__',)

这意味着:

  • 名字、文档、注解、模块都会复制
  • wrapper 的 __dict__ 会更新,而不是覆盖

🟩 5. wraps() 就是对 update_wrapper 的封装

等价于:

def wraps(wrapped):
    return partial(update_wrapper, wrapped=wrapped)

所以这两段代码效果一致:

@wraps(func)

等价于:

update_wrapper(wrapper, func)

🔥 6. 实战示例:处理自定义属性

假设你写一个装饰器,需要保留原函数自定义属性:

def tag(t):
    def decorator(fn):
        fn.tag = t
        return fn
    return decorator

使用 update_wrapper 自动复制:

def deco(fn):
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)

    update_wrapper(wrapper, fn)
    return wrapper

⭐ 7. 复杂示例:多层装饰器时保持 metadata 正确性

from functools import update_wrapper

def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    update_wrapper(wrapper, func)  # 保留元信息
    return wrapper

def another_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    update_wrapper(wrapper, func)  # 再次保留元信息
    return wrapper


@another_decorator
@decorator
def hello():
    """Hello Doc"""
    pass

print(hello.__doc__)  # Hello Doc
print(hello.__name__)  # hello

如果不使用 update_wrapper,函数会被多次 “包装损坏”。

☘️ 8. update_wrapper 能解决什么实际问题?

问题 不用 update_wrapper 用了 update_wrapper
函数名变成 wrapper
文档丢失
FastAPI 文档报错
IDE 参数提示无效
lru_cache 不能工作
调试(traceback)难看懂
recursive 调用失败

🟧 9. 专业使用:指定复制哪些字段

例如你只想复制名字和文档:

update_wrapper(wrapper, func, assigned=('__name__', '__doc__'), updated=())

🟣 10. update_wrapper 的逆操作:获取原函数

wrapper.__wrapped__

update_wrapper 会自动加这个属性。

🟨 11. 最推荐的封装方式(你项目可用)

写你自己的装饰器基类:

from functools import update_wrapper

def preserve(fn):
    def decorator(wrapper):
        return update_wrapper(wrapper, fn)
    return decorator

使用:

def debug(fn):
    @preserve(fn)
    def wrapper(*args, **kwargs):
        print(fn.__name__)
        return fn(*args, **kwargs)
    return wrapper

对你所有装饰器统一保留 metadata。

🎯 总结(速查)

update_wrapper(wrapper, wrapped):

  • 将 wrapped 的元数据复制到 wrapper
  • 是 wraps() 的底层
  • 保留 __name__, __doc__, __annotations__, __module__
  • 添加 __wrapped__ 属性
  • 对编写装饰器必不可少