Python|functools.wraps 装饰器元信息

一、没有 @wraps 时发生了什么?

def log_decorator(func):      # 此时 func = 原始的 test 函数
    def inner(*args, **kwargs):
        print("执行前")
        return func(*args, **kwargs)
    return inner              # 返回 inner 函数

@log_decorator
def test():                   # 这行代码执行完毕后,test 被重新赋值了!
    """这是test函数文档"""
    pass

# @log_decorator 等价于执行:
# test = log_decorator(test)  # ← test 不再指向原函数,而是指向 inner

关键理解:

  • 当你写 @log_decorator 时,Python 会立即执行 test = log_decorator(test)
  • log_decorator 返回的是 inner 函数
  • 所以 test 这个名字被重新绑定到了 inner
  • 原始的 test 函数已经丢失了! (没有任何变量指向它)

这就是为什么打印 test.name 会输出 inner——因为 test 现在就是 inner,是那个内部包装函数的名字。


二、@wraps 做了什么?

import functools

def log_decorator(func):
    @functools.wraps(func)   # 这行是关键!
    def inner(*args, **kwargs):
        print("执行前")
        return func(*args, **kwargs)
    return inner

@functools.wraps(func) 等价于执行:

def inner(*args, **kwargs):
    print("执行前")
    return func(*args, **kwargs)

# wraps 做的事:
inner.__name__ = func.__name__      # 把原函数的名字复制过来
inner.__doc__ = func.__doc__        # 把原函数的文档复制过来
inner.__module__ = func.__module__  # 把原函数的模块复制过来
# ... 还复制了很多其他元信息

关键理解:

  • @wraps 没有恢复原始函数test 仍然指向 inner,不是原始函数
  • @wraps 只是把原函数的元信息(名字、文档、注解等)复制到 inner 上
  • 所以 test.__name__ 显示的是被复制过来的值,变成了 'test'

三、图解整个过程

没有 @wraps 时:

原始函数: test → function (name='test', doc='这是test函数文档')
                                              ↓
                                 @log_decorator 执行
                                              ↓
                              test = log_decorator(test)
                                              ↓
现在: test → inner (name='inner', doc=None)  ← 原始的 test 函数丢失了

有 @wraps 时:

原始函数: test → function (name='test', doc='这是test函数文档')
                                              ↓
                                 @log_decorator 执行
                                              ↓
                              test = log_decorator(test)
                                              ↓
                              @wraps 把元信息复制过来
                                              ↓
现在: test → inner (name='test', doc='这是test函数文档')  ← 复制了元信息

四、为什么 @wraps 很重要?

没有 @wraps 会导致各种问题:

  1. 调试困难:报错时显示 inner 而不是 test,找不到问题来源
  2. 文档丢失help(test) 显示的是 inner 的文档(通常是空的)
  3. 框架功能失效:一些框架(如 Flask、FastAPI)依赖函数名来做路由映射
  4. 装饰器嵌套时层层丢失:函数名变成 inner1inner2inner3……无法追踪
posted @ 2026-08-17 17:46  静心笃行。  阅读(15)  评论(0)    收藏  举报