文章目录
- 深入理解 Python 的 `__wrapped__` 属性
- 一、什么是 `__wrapped__`?
- 二、为什么需要 `__wrapped__`?
- 三、`functools.wraps` 的作用
- 四、`__wrapped__` 的核心用途
- 1. 访问原始函数
- 2. 支持函数签名检查(inspect)
- 3. 多层装饰器链
- 4. 调试和工具支持
- 五、实现原理简析
- 六、最佳实践
- ✅ 总是使用 `functools.wraps`
- ❗ 不要手动忽略 `__wrapped__`
- 七、总结
深入理解 Python 的__wrapped__属性
在 Python 的装饰器(decorator)体系中,函数包装(wrapping)是一种非常常见的技术。然而,函数一旦被装饰,原始函数的元信息(如名称、文档字符串、签名等)往往会被覆盖。为了解决这个问题,Python 引入了一个非常关键但容易被忽视的属性:__wrapped__。
本文将系统介绍__wrapped__的作用、原理以及实际应用场景。
一、什么是__wrapped__?
__wrapped__是一个函数属性,用于指向被装饰前的原始函数。
简单来说:
wrapped_function.__wrapped__==original_function这个属性通常由标准库中的functools.wraps自动设置。
二、为什么需要__wrapped__?
考虑一个最简单的装饰器:
defmy_decorator(func):defwrapper(*args,**kwargs):print("Before call")returnfunc(*args,**kwargs)returnwrapper使用它:
@my_decoratordefadd(a,b):returna+b此时:
print(add.__name__)# 输出: wrapper问题出现了:原函数add的信息丢失了。
三、functools.wraps的作用
为了解决这个问题,我们通常这样写:
fromfunctoolsimportwrapsdefmy_decorator(func):@wraps(func)defwrapper(*args,**kwargs):print("Before call")returnfunc(*args,**kwargs)returnwrapper@wraps(func)做了几件关键的事情:
- 复制元信息(如
__name__,__doc__) - 设置
__wrapped__属性
等价于:
wrapper.__wrapped__=func四、__wrapped__的核心用途
1. 访问原始函数
print(add.__wrapped__)# <function add at ...>甚至可以直接调用原始函数:
result=add.__wrapped__(2,3)print(result)# 5(跳过装饰器)2. 支持函数签名检查(inspect)
Python 的inspect模块会使用__wrapped__来获取真实函数签名:
importinspectprint(inspect.signature(add))如果没有__wrapped__,签名会变成:
(*args, **kwargs)而不是:
(a, b)3. 多层装饰器链
当有多个装饰器时:
@decorator1@decorator2deffunc():pass会形成链式结构:
func->wrapper1->wrapper2->original可以通过不断访问:
func.__wrapped__.__wrapped__逐层回溯到最初的函数。
4. 调试和工具支持
很多工具(如:
- 调试器
- 文档生成工具
- 类型检查工具
)都会依赖__wrapped__来“还原”真实函数。
五、实现原理简析
functools.wraps本质上是一个装饰器工厂:
defwraps(wrapped):defdecorator(wrapper):wrapper.__wrapped__=wrappedreturnwrapperreturndecorator实际实现中还会复制:
__module____name____qualname____doc____annotations__
六、最佳实践
✅ 总是使用functools.wraps
fromfunctoolsimportwraps这是推荐的标准写法,否则:
- 调试困难
- introspection 失效
- 工具链支持变差
❗ 不要手动忽略__wrapped__
如果你写自定义装饰器但不设置__wrapped__,可能导致:
inspect失效- FastAPI / Click 等框架行为异常
七、总结
__wrapped__虽然是一个小属性,但在 Python 生态中扮演着关键角色:
- 保留函数原始引用
- 支持 introspection(自省)
- 让工具链正常工作
- 提升调试体验
一句话总结:
__wrapped__是装饰器世界中的“回溯指针”。