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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python lazypredict构建大量基本模型简化机器学习

    python lazypredict构建大量基本模型简化机器学习

    这篇文章主要介绍了python lazypredict构建大量基本模型简化机器学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • python 将html转换为pdf的几种方法

    python 将html转换为pdf的几种方法

    这篇文章主要介绍了python 将html转换为pdf的几种方法,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2020-12-12
  • 使用Python实现音频降噪功能

    使用Python实现音频降噪功能

    在音频处理领域,背景噪声是一个常见的问题,为了提高音频的质量,我们需要对音频进行降噪处理,本文将介绍如何使用 Python 实现音频降噪,文中通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-11-11
  • Python安装Bs4的多种方法

    Python安装Bs4的多种方法

    这篇文章主要介绍了Python安装Bs4几种方法,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Python实现快速替换Word文档中的关键字

    Python实现快速替换Word文档中的关键字

    使用Python自动化处理Word文档可以帮助您提高效率,并减少手动处理文档所需的时间和精力,所以本文为大家准备了Python快速替换Word文档中的关键字的方法,希望对大家有所帮助
    2023-06-06
  • python和anaconda区别以及先后安装的问题详解

    python和anaconda区别以及先后安装的问题详解

    Anaconda(开源的Python包管理器)是一个python发行版,包含了conda、Python等180多个科学包及其依赖项,下面这篇文章主要给大家介绍了关于python和anaconda区别以及先后安装问题的相关资料,需要的朋友可以参考下
    2022-05-05
  • 使用OpenCV为图像加水印的教程

    使用OpenCV为图像加水印的教程

    通过本文学习将学会如何使用 OpenCV 为多个图像添加水印,在 OpenCV 中调整图像大小也很方便,对OpenCV图像加水印相关知识感兴趣的朋友一起看看吧
    2021-09-09
  • Python NumPy实现数组排序与过滤示例分析讲解

    Python NumPy实现数组排序与过滤示例分析讲解

    NumPy是Python的一种开源的数值计算扩展,它支持大量的维度数组与矩阵运算,这篇文章主要介绍了使用NumPy实现数组排序与过滤的方法,需要的朋友们下面随着小编来一起学习吧
    2023-05-05
  • python网络编程之TCP通信实例和socketserver框架使用例子

    python网络编程之TCP通信实例和socketserver框架使用例子

    这篇文章主要介绍了python网络编程之TCP通信实例和socketserver框架使用例子,需要的朋友可以参考下
    2014-04-04
  • Python分支语句常见的使用方法

    Python分支语句常见的使用方法

    这篇文章主要介绍了Python分支语句常见的使用方法,Python分支语句,也称为选择语句,体现了程序的选择结构,即对应不同的场景,选择不同的处理方式,具体常见的用法需要的朋友可参考下面文章内容
    2022-06-06

最新评论