Python 装饰器如何影响函数签名?

Python装饰器默认覆盖原函数签名(如__name__、__doc__等),因闭包函数与原函数是不同对象;用functools.wraps可自动复制元信息,确保调试、文档生成和框架反射正常。

Python 装饰器默认会覆盖原函数的签名(如 __name____doc____annotations__ 等),导致调试、文档生成、类型检查或框架反射(如 FastAPI、Flask)出错。关键在于装饰器内部返回的新函数没有继承原函数的元信息。

为什么签名会被改写?

典型装饰器返回一个闭包函数,它与原函数是完全不同的对象:

  • 新函数的 __name__ 是闭包名(如 wrapper),不是原函数名
  • __doc__ 默认为 None 或闭包自身的文档字符串
  • __annotations____module____qualname__ 等也丢失
  • 参数签名(通过 inspect.signature() 获取)变成闭包的参数,而非原函数的

如何保留原始签名?

使用 functools.wraps 是最标准、推荐的做法。它把原函数的关键属性复制到包装函数上:

from functools import wraps

def my_decorator(func): @wraps(func) # ← 关键:自动复制 name, doc, annotations 等 def wrapper(*args, *kwargs): print("Before") result = func(args, **kwargs) print("After") return result return wrapper

@my_decorator def greet(name: str) -> str: """Say hello to someone.""" return f"Hello, {name}"

此时 greet.__name__ 仍是 "greet"greet.__doc__ 仍是 "Say hello to someone."inspect.signature(greet) 也正确显示 (name: str) -> str

手动修复签名(不推荐,仅作了解)

若不用 @wraps,可手动赋值,但易遗漏:

  • wrapper.__name__ = func.__name__
  • wrapper.__doc__ = func.__doc__
  • wrapper.__annotations__ = func.__annotations__
  • wrapper.__module__ = func.__module__
  • 还需处理 __qualname____dict__ 等,容易出错

带参数的装饰器要注意什么?

多一层嵌套时,@wraps 仍要作用于最内层包装函数:

def repeat(times: int):
    def decorator(func):
        @wraps(func)  # ← 放在 inner wrapper 上,不是外层 decorator
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    re

turn decorator

漏掉这行,即使装饰器本身有参数,签名依然丢失。