人人都会AI编程

functools.wraps 作用与标准写法

更新时间:2026-07-12

当你写装饰器时,如果不加任何处理,被装饰函数的一些元信息会丢失——函数的名字、文档字符串、参数列表等会被换成装饰器内部包装函数的元信息。这会导致调试困难、文档生成出错、反射等特性失效。

functools.wraps 就是专门解决这个问题的标准工具。下面先看问题,再看标准解法。

没有 wraps 时的“元信息丢失”
def my_decorator(func):
    def wrapper(*args, **kwargs):
        """这是 wrapper 的文档"""
        print("调用前")
        result = func(*args, **kwargs)
        print("调用后")
        return result
    return wrapper

@my_decorator
def greet(name):
    """向用户打招呼"""
    print(f"你好,{name}")

print(greet.__name__)   # 输出: wrapper (应该是 greet)
print(greet.__doc__)    # 输出: 这是 wrapper 的文档 (应该是 "向用户打招呼")

这里 greet 表面上还是那个 greet,但实际上已经是 wrapper 了,它的名字和文档都变掉了。当你在 Flask 里用 @app.route 装饰视图函数时,如果函数名都一样,路由就会冲突;生成自动化文档时也会拿到错误的说明。

标准写法:使用 @wraps(func)

wraps 是一个装饰器工厂,它会把原函数 func 的关键元信息复制到包装函数 wrapper 上,让 wrapper 在外观上“看起来像”原函数。

from functools import wraps

def my_decorator(func):
    @wraps(func)          # 标准写法:在 wrapper 前加上这一行
    def wrapper(*args, **kwargs):
        """这是 wrapper 的文档"""
        print("调用前")
        result = func(*args, **kwargs)
        print("调用后")
        return result
    return wrapper

@my_decorator
def greet(name):
    """向用户打招呼"""
    print(f"你好,{name}")

print(greet.__name__)   # 输出: greet
print(greet.__doc__)    # 输出: 向用户打招呼

经过 @wraps(func) 修饰后,greetnamedocmoduleannotations 等属性都得到了保留,甚至 inspect 模块拿到的参数信息也是正确的。

wraps 还能做什么

wraps 默认复制的属性包括:
module, name, qualname, annotations, doc, dict
你也可以通过参数手动指定要复制的属性,或者排除某些属性:

@wraps(func, assigned=('__name__', '__doc__'))
实用原则

只要写带参数的装饰器,就一定要在 wrapper 函数上方加 @wraps(func)。这几乎是一个没有副作用的习惯,只会让你的代码更健壮、更可调试。大部分代码检查工具(如 pylint、flake8)也会提示你补上这一行。

所以,记住标准模板:

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 装饰逻辑
        return func(*args, **kwargs)
    return wrapper

以及带参数的装饰器:

from functools import wraps

def decorator_with_args(arg1, arg2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 使用 arg1, arg2
            return func(*args, **kwargs)
        return wrapper
    return decorator

掌握这个固定格式,就能写出专业且不易出错的装饰器。