Python装饰器中常用的functools.wraps的使用

 更新时间:2025年08月29日 09:49:01   作者:青衫客36  
functools.wraps是Python装饰器开发中的关键工具,下面就就来介绍一下Python装饰器中常用的functools.wraps的使用,感兴趣的可以了解一下

functools.wraps 是 Python 装饰器开发中非常关键、但常被忽视的工具。它不仅是代码“优雅性”的体现,还直接影响调试、文档生成、反射等功能的准确性。

一、什么是functools.wraps?

functools.wraps 是一个装饰器,它用于 将被装饰函数的元信息(如名称、文档、注解等)复制到装饰器内部的包装函数上。

为什么需要它?

来看一个没有使用 wraps 的装饰器:

def logger(func):
    def wrapper(*args, **kwargs):
        print("调用函数")
        return func(*args, **kwargs)
    return wrapper

@logger
def add(a, b):
    """计算两个数的和"""
    return a + b

此时:

print(add.__name__)     # ❌ wrapper
print(add.__doc__)      # ❌ None

我们调用的是 add(),但其实 add 是 wrapper,元信息丢失了。

二、解决方案:使用functools.wraps

from functools import wraps

def logger(func):
    @wraps(func)  # 👈 关键:复制原函数的元信息
    def wrapper(*args, **kwargs):
        print("调用函数")
        return func(*args, **kwargs)
    return wrapper

@logger
def add(a, b):
    """计算两个数的和"""
    return a + b

现在输出:

print(add.__name__)  # ✅ add
print(add.__doc__)   # ✅ 计算两个数的和

三、底层原理分析:@wraps做了什么?

functools.wrapsfunctools.update_wrapper 的语法糖。

等价于:

@wraps(func)
def wrapper(): ...

# 等价于
def wrapper(): ...
wrapper = functools.update_wrapper(wrapper, func)

update_wrapper(wrapper, func)会做这些事:

属性被复制
__module__模块名
__name__函数名
__qualname__完整限定名(含类名)
__annotations__参数类型注解
__doc__文档字符串
__dict__自定义属性字典(保持装饰器后可追加属性)

四、源码分析:wraps的定义

来自 Python 标准库 functools.py

def wraps(wrapped,
          assigned=WRAPPER_ASSIGNMENTS,     # 默认:__module__, __name__, __qualname__, __annotations__, __doc__
          updated=WRAPPER_UPDATES):         # 默认:__dict__
    def decorator(wrapper):
        return update_wrapper(wrapper, wrapped, assigned, updated)
    return decorator

所以 @wraps(func) 是返回了一个“装饰器”,给你的 wrapper() 做属性复制。

五、实用场景总结

场景说明
✅ 调试保留原函数名,调试信息更准确
✅ 文档生成help(func)、Sphinx 等能读取 docstring
✅ 反射与 introspectioninspect.getfullargspec(func) 能得到正确参数信息
✅ functools.cache / lru_cache依赖函数的 __name__ 和 __hash__,否则缓存可能出错
✅ unittest mock.patchpatch 也需要定位原函数,名字不能丢
✅ IDE提示 / 自动补全wrapper.__annotations__ 有利于类型推断和补全支持

六、完整示例对比

1. 单层嵌套装饰器

❌ 不使用wraps

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

@log
def hello(name: str):
    """打招呼"""
    return f"Hi, {name}"

print(hello.__name__)        # wrapper
print(hello.__doc__)         # None
print(hello.__annotations__) # {}

✅ 使用wraps

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

输出:

hello.__name__         # hello
hello.__doc__          # 打招呼
hello.__annotations__  # {'name': <class 'str'>}

2. 多层嵌套装饰器(以2层嵌套为例)

❌ 不使用wraps

def decorator1(func):
    def wrapper(*args, **kwargs):
        """wrapper doc 1"""
        return func(*args, **kwargs)

    return wrapper


def decorator2(func):
    def wrapper(*args, **kwargs):
        """wrapper doc 2"""
        return func(*args, **kwargs)

    return wrapper


@decorator1
@decorator2
def my_function():
    """This is the original function"""
    print("Hello")


print(my_function.__name__)  # wrapper
print(my_function.__doc__)  # wrapper doc 1

多个装饰器会逐层覆盖原函数的元信息(metadata),比如 __name__、__doc__。在上述程序中,decorator2 返回一个新的函数 wrapper,decorator1 再包装这个新的函数,得到另一个新的 wrapper,每一层 wrapper 都是新的函数对象,并没有自动保留原函数的 __name__、__doc__、__annotations__ 等属性。

✅ 使用wraps

from functools import wraps


def decorator1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper doc 1"""
        return func(*args, **kwargs)

    return wrapper


def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper doc 2"""
        return func(*args, **kwargs)

    return wrapper


@decorator1
@decorator2
def my_function():
    """This is the original function"""
    print("Hello")


print(my_function.__name__)  # my_function
print(my_function.__doc__)  # This is the original function

对于多层装饰器的情况,建议每一层装饰器都应该加 @wraps(func),否则元信息只保留最外层那一层的,并且在多层装饰器链中,调试会非常混乱

⚠️ 七、常见误区

错误原因
忘记加 @wraps实际上丢掉了原函数元信息
wraps() 用错了对象必须传的是“要包装的原函数”
把 wraps 当成执行函数它本身是个“返回装饰器的函数”
多层嵌套装饰器未层层使用 wraps每一层都需要加 @wraps(func)

🧠 八、总结

  • ✅ 凡是写装饰器 ➜ wrapper 外面加 @wraps(func)
  • ✅ 目的是:保留函数元信息,防止“身份丢失”
  • ✅ 等价于:update_wrapper(wrapper, func)
  • ✅ 一般配合 functools 系列使用,如:@lru_cache、@cache_property

📌 最佳实践模板

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 执行前逻辑
        result = func(*args, **kwargs)
        # 执行后逻辑
        return result
    return wrapper

到此这篇关于Python装饰器中常用的functools.wraps的使用的文章就介绍到这了,更多相关Python functools.wraps内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Django中如何使用sass的方法步骤

    Django中如何使用sass的方法步骤

    这篇文章主要介绍了Django中如何使用sass的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • python自定义函数中的return和print使用及说明

    python自定义函数中的return和print使用及说明

    这篇文章主要介绍了python自定义函数中的return和print使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • python颜色随机生成器的实例代码

    python颜色随机生成器的实例代码

    这篇文章主要介绍了python颜色随机生成器的实例代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • 详解利用Python制作中文汉字雨效果

    详解利用Python制作中文汉字雨效果

    这篇文章主要介绍了如何利用Python中的Pygame模块实现中文汉字雨效果,文中通过示例代码介绍的非常详细,感兴趣的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • Python pyecharts绘制柱状图

    Python pyecharts绘制柱状图

    这篇文章主要介绍了Python pyecharts绘制柱状图,文章介绍的柱状/条形图,通过柱形的高度/条形的宽度来表现数据的大小,感兴趣的小伙伴一起进入文章学习更详细内容吧
    2021-12-12
  • 利用python循环创建多个文件的方法

    利用python循环创建多个文件的方法

    今天小编就为大家分享一篇利用python循环创建多个文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • 对Python中创建进程的两种方式以及进程池详解

    对Python中创建进程的两种方式以及进程池详解

    今天小编就为大家分享一篇对Python中创建进程的两种方式以及进程池详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • 巧妙使用python opencv库玩转视频帧率

    巧妙使用python opencv库玩转视频帧率

    这篇文章主要介绍了巧妙使用python opencv库玩转视频帧率的教程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • python redis连接 有序集合去重的代码

    python redis连接 有序集合去重的代码

    这篇文章主要介绍了python redis连接 有序集合去重的代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-08-08
  • Python自动化运维_文件内容差异对比分析

    Python自动化运维_文件内容差异对比分析

    下面小编就为大家分享一篇Python自动化运维_文件内容差异对比分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12

最新评论