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 wrapsdef 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。
立即学习“Python免费学习笔记(深入)”;
手动修复签名(不推荐,仅作了解)
若不用 @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
return decorator
漏掉这行,即使装饰器本身有参数,签名依然丢失。










