python @wraps()装饰器的使用

 更新时间:2026年03月22日 10:13:41   作者:Yorlen_Zhang  
functools.wraps是Python装饰器的关键工具,用于保留被装饰函数的元信息,本文就来详细的介绍一下@wraps()装饰器的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

“它让装饰器不‘穿帮’,被装饰的函数还能假装自己是原来的函数。”

下面从 0 到源码,带你彻底看懂。

一、为什么需要 wraps?

装饰器会“偷梁换柱”

def dec(f):
    def wrapper(*a, **kw):
        return f(*a, **kw)
    return wrapper

@dec
def add(x, y):
    """add two numbers"""
    return x + y

print(add.__name__)   # wrapper   ← 名字被换掉了!
print(add.__doc__)    # None      ← 文档串也没了

结果调试、IDE 提示、Sphinx 文档生成全都认错人。

functools.wraps 的作用
把原函数的 名字、文档、参数表、注解、模块名、属性字典 等关键元信息一次性拷贝到包装器上,解决“穿帮”问题。

二、最简正确姿势

from functools import wraps
def dec(f):
    @wraps(f)            # 就加这一行
    def wrapper(*a, **kw):
        """wrapper doc"""
        print('before')
        return f(*a, **kw)
    return wrapper
@dec
def add(x, y):
    """add two numbers"""
    return x + y
print(add.__name__)      # add
print(add.__doc__)       # add two numbers
print(add.__annotations__)  # 原函数注解也保留

三、wraps 其实是个“偏函数”

源码等价于:

def wraps(wrapped,
          assigned=WRAPPER_ASSIGNMENTS,      # 要拷贝的七大属性
          updated=WRAPPER_UPDATES):          # __dict__ 如何合并
    return partial(update_wrapper,          # 返回一个“等待 wrapper 作为参数”的函数
                   wrapped=wrapped,
                   assigned=assigned,
                   updated=updated)

所以
@wraps(func) 先制造一个“待装饰器”,再把真正的 wrapper 传进去,完成 update_wrapper(wrapper, func)

四、七大默认拷贝属性(WRAPPER_ASSIGNMENTS)

  • __module__
  • __name__
  • __qualname__ (3.3+ 限定名,嵌套类/函数需要)
  • __doc__
  • __annotations__ (3.10+ 包括 PEP 563 延迟求值)
  • __dict__ 的部分合并规则(见 WRAPPER_UPDATES)
  • __kwdefaults____defaults__ 默认参数值

五、自定义要保留/跳过的属性

MY_ATTRS = ('__name__', '__doc__', '__my_flag__')

def my_wraps(f):
    return wraps(f, assigned=MY_ATTRS)

# 或者只想忽略某个属性
from functools import WRAPPER_ASSIGNMENTS
skip_annotation = tuple(a for a in WRAPPER_ASSIGNMENTS if a != '__annotations__')

def dec(f):
    @wraps(f, assigned=skip_annotation)
    def wrapper(*a, **kw):
        ...

六、保留 wrapper 自己的属性(叠加)

有时既想保留原函数,又想给 wrapper 加新属性,用 updated 参数:

def dec(f):
    @wraps(f, updated=())   # 不合并 __dict__,避免覆盖
    def wrapper(*a, **kw):
        ...
    wrapper.decorator = 'my_dec'   # 新增标记
    return wrapper

七、带参数的装饰器 + wraps

from functools import wraps

def repeat(n):                      # ← 这一层接收参数
    def dec(f):
        @wraps(f)
        def wrapper(*a, **kw):
            for _ in range(n):
                result = f(*a, **kw)
            return result
        return wrapper
    return dec

@repeat(3)
def hello():
    """say hello"""
    print('hi')

hello()
print(hello.__name__)   # hello
print(hello.__doc__)    # say hello

关键点:@wraps(f) 一定要放在最内层 wrapper 上,而不是 dec 本身。

八、类装饰器 & 方法也能用

def count_calls(cls):
    orig_init = cls.__init__

    @wraps(orig_init)          # 保留原 __init__ 签名
    def new_init(self, *a, **kw):
        self._cnt = 0
        orig_init(self, *a, **kw)

    cls.__init__ = new_init
    return cls

九、调试技巧:一眼看穿“真身”

>>> hello.__wrapped__        # wraps 自动加的秘密属性
<function hello at 0x...>    # 指向原始函数
>>> inspect.signature(hello) # 拿到的是原签名,不会看到 wrapper 的 *a, **kw

十、常见坑速查

现象原因解决
TypeError: wraps() missing 1 required positional argument把 @wraps 写成了 @wraps()正确写 @wraps(func)
签名还是 wrapper 的 (*args, **kwargs)用了 wraps 但 IDE 仍显示通用签名IDE 缓存 / 需 inspect.signature 重新提取
保留不了自定义属性默认 assigned 不包含传自定义列表

十一、一句话总结

写装饰器就两步:

  • 先 from functools import wraps
  • 在 wrapper 函数正上方写 @wraps(被装饰函数)

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

相关文章

  • python中for语句简单遍历数据的方法

    python中for语句简单遍历数据的方法

    这篇文章主要介绍了python中for语句简单遍历数据的方法,以一个简单实例形式分析了Python中for语句遍历数据的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-05-05
  • Python银行系统实战源码

    Python银行系统实战源码

    这篇文章主要为大家详细介绍了Python银行系统实战源码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • Python编程基础之函数和模块

    Python编程基础之函数和模块

    这篇文章主要为大家介绍了Python函数和模块,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • python在每个字符后添加空格的实例

    python在每个字符后添加空格的实例

    今天小编就为大家分享一篇python在每个字符后添加空格的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • 用python 绘制茎叶图和复合饼图

    用python 绘制茎叶图和复合饼图

    这篇文章主要介绍了用python 绘制茎叶图和复合饼图,帮助大家更好的理解和学习使用python,感兴趣的朋友可以了解下
    2021-02-02
  • Python+wxPython打造智能网页截图工具

    Python+wxPython打造智能网页截图工具

    在网站测试、UI审查或文档编写过程中,我们常常需要对网站的所有页面进行截图记录,下面我们就来看看如何使用Python开发一个自动化工具,实现一键遍历网站所有链接并生成带截图的Excel报告吧
    2025-10-10
  • 基于python实现高速视频传输程序

    基于python实现高速视频传输程序

    这篇文章主要介绍了基于python实现高速视频传输程序的实例代码,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • python实现简易名片管理系统

    python实现简易名片管理系统

    这篇文章主要为大家详细介绍了python实现简易名片管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • Python turtle绘图教程之七段数码管显示数字和字母

    Python turtle绘图教程之七段数码管显示数字和字母

    这篇文章主要给大家介绍了关于Python turtle绘图教程之七段数码管显示数字和字母的相关资料,Python是一种流行的编程语言,可用于编写各种类型的程序,在数码管显示器上数字8由7条不同的线条组成,需要的朋友可以参考下
    2023-10-10
  • tensorflow-gpu安装的常见问题及解决方案

    tensorflow-gpu安装的常见问题及解决方案

    这篇文章主要介绍了tensorflow-gpu安装的常见问题及解决方案,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧,需要的朋友可以参考下
    2020-01-01

最新评论