一、wrapt 库概述
wrapt 是一款专注于 Python 装饰器与函数包装的轻量级库,核心用于稳定、标准地实现函数、类、方法的包装,解决原生装饰器破坏函数元信息、签名异常、嵌套失效等问题。其基于描述符与调用协议工作,保留原对象属性,兼容性强、稳定性高,无额外依赖。该库采用 BSD 许可证,开源免费,适合生产环境使用,缺点是功能聚焦包装,无扩展能力。

二、wrapt 库安装方法
wrapt 支持 Python 3.6+ 所有版本,跨平台兼容,安装方式简洁,使用 pip 即可完成安装,命令如下:
pip install wrapt
安装完成后,可在 Python 交互环境中执行导入命令验证:
import wrapt
print(wrapt.__version__)
若能正常打印版本号,说明安装成功。该库体积极小,安装速度快,不会对项目环境造成额外负担,无论是小型脚本还是大型工程,都可放心引入。
三、wrapt 核心基础使用
3.1 基础装饰器实现
原生 Python 装饰器会导致被装饰函数的 __name__、__doc__ 等元信息丢失,调用签名异常,而 wrapt 可完美解决该问题。使用 @wrapt.decorator 装饰包装函数,即可创建标准装饰器。
import wrapt
# 定义基础装饰器
def simple_decorator(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
# 执行前逻辑
print(f"函数 {wrapped.__name__} 即将执行")
# 调用原函数
result = wrapped(*args, **kwargs)
# 执行后逻辑
print(f"函数 {wrapped.__name__} 执行完成")
return result
return wrapper(func)
# 测试函数
@simple_decorator
def test_function(name):
"""这是一个测试函数"""
print(f"Hello, {name}")
# 调用函数
test_function("wrapt")
# 查看元信息,未被破坏
print("函数名称:", test_function.__name__)
print("函数文档:", test_function.__doc__)
代码说明:
wrapped代表被包装的原函数,可直接调用并传递参数;instance为类方法中的实例,普通函数中为None;args和kwargs分别为位置参数和关键字参数,完整传递给原函数;- 装饰后的函数保留原名称、文档字符串,解决原生装饰器痛点。
3.2 无侵入式函数包装
wrapt 支持不使用语法糖,直接对现有函数进行动态包装,适合对第三方库函数、内置函数进行增强,无需修改原函数代码。
import wrapt
# 定义包装逻辑
def log_wrapper(wrapped, instance, args, kwargs):
print(f"[日志] 调用函数: {wrapped.__name__}")
return wrapped(*args, **kwargs)
# 原函数
def add(a, b):
return a + b
# 动态包装函数
wrapped_add = wrapt.wrap_function_wrapper(add, log_wrapper)
# 调用包装后的函数
print(add(1, 2))
print(wrapped_add(3, 4))
代码说明:
wrapt.wrap_function_wrapper可直接接收原函数和包装函数,返回新的包装对象;- 原函数不受影响,可同时使用原函数和包装后的函数,灵活适配不同场景。
3.3 类与实例方法包装
wrapt 对类方法、静态方法、实例方法的支持十分完善,会自动识别方法类型,正确传递 instance 参数,无需额外处理。
import wrapt
# 定义装饰器
def method_decorator(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
if instance is not None:
print(f"类 {instance.__class__.__name__} 的方法执行")
return wrapped(*args, **kwargs)
return wrapper(func)
# 测试类
class TestClass:
@method_decorator
def instance_method(self):
print("执行实例方法")
@staticmethod
@method_decorator
def static_method():
print("执行静态方法")
# 调用
obj = TestClass()
obj.instance_method()
TestClass.static_method()
代码说明:
- 实例方法中
instance为类的实例对象,可通过其访问类属性与实例属性; - 静态方法中
instance为None,装饰器逻辑可自动适配不同类型方法; - 无需为不同方法编写不同装饰器,一套逻辑通用。
3.4 带参数的装饰器实现
原生 Python 实现带参数的装饰器需要多层嵌套,代码可读性差,wrapt 可简化带参装饰器的编写,结构清晰易懂。
import wrapt
# 带参数的装饰器
def decorator_with_args(msg):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print(f"自定义信息: {msg}")
return wrapped(*args, **kwargs)
return wrapper
# 使用带参装饰器
@decorator_with_args("这是自定义提示信息")
def demo_function():
print("执行演示函数")
demo_function()
代码说明:
- 外层函数接收装饰器参数,内层通过
@wrapt.decorator实现包装; - 无需多层嵌套函数,代码层级简洁,易于维护和扩展。
四、wrapt 高级功能使用
4.1 函数签名保留
在开发接口、工具库时,函数签名十分重要,原生装饰器会破坏 inspect 模块获取的签名,wrapt 可完整保留。
import wrapt
import inspect
# 装饰器
def signature_decorator(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
return wrapper(func)
# 带签名的函数
@signature_decorator
def complex_func(a: int, b: str, c: list = None) -> dict:
return {"a": a, "b": b, "c": c}
# 获取函数签名
print(inspect.signature(complex_func))
代码说明:
- 执行后可正常打印参数类型、默认值、返回值类型;
- 适合开发 SDK、框架、对外接口,保证调用提示与文档准确性。
4.2 嵌套装饰器兼容
多个装饰器嵌套时,原生装饰器容易出现执行顺序混乱、元信息覆盖问题,wrapt 可保证嵌套稳定执行。
import wrapt
# 第一个装饰器
def decorator1(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print("装饰器1 执行")
return wrapped(*args, **kwargs)
return wrapper(func)
# 第二个装饰器
def decorator2(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print("装饰器2 执行")
return wrapped(*args, **kwargs)
return wrapper(func)
# 嵌套使用
@decorator1
@decorator2
def nested_func():
print("原函数执行")
nested_func()
代码说明:
- 执行顺序严格遵循装饰器从上到下、原函数最后执行的规则;
- 多层嵌套后元信息依然完整,无异常冲突。
4.3 类的整体包装
wrapt 不仅能包装方法,还能对整个类进行包装,批量增强类中所有方法,减少重复代码。
import wrapt
# 类包装器
def class_wrapper(wrapped, instance, args, kwargs):
print(f"初始化类: {wrapped.__name__}")
return wrapped(*args, **kwargs)
# 包装类
@wrapt.decorator
def log_class(wrapped, instance, args, kwargs):
return class_wrapper(wrapped, instance, args, kwargs)
@log_class
class User:
def __init__(self, name):
self.name = name
user = User("test")
代码说明:
- 可对类的初始化、实例化过程进行拦截;
- 适合日志记录、权限校验、参数验证等全局逻辑。
4.4 内置函数与第三方库包装
实际开发中常需增强内置函数或第三方库函数,wrapt 可在不修改源码的前提下完成包装。
import wrapt
import math
# 包装 math.sqrt 函数
original_sqrt = math.sqrt
@wrapt.decorator
def sqrt_wrapper(wrapped, instance, args, kwargs):
num = args[0]
print(f"计算平方根: {num}")
if num < 0:
raise ValueError("负数不能计算平方根")
return wrapped(*args, **kwargs)
math.sqrt = sqrt_wrapper(math.sqrt)
# 测试
print(math.sqrt(16))
# print(math.sqrt(-4)) # 会触发异常
代码说明:
- 直接替换模块中的函数,对所有调用生效;
- 可用于参数校验、异常捕获、性能统计、日志埋点等场景。
五、实际项目综合案例
5.1 接口请求耗时统计装饰器
在 Web 开发、接口调用场景中,经常需要统计函数执行耗时,使用 wrapt 可快速实现通用耗时统计装饰器。
import wrapt
import time
# 耗时统计装饰器
def time_counter(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
start_time = time.time()
try:
result = wrapped(*args, **kwargs)
return result
finally:
end_time = time.time()
cost_time = round((end_time - start_time) * 1000, 2)
print(f"函数 {wrapped.__name__} 执行耗时: {cost_time} ms")
return wrapper(func)
# 模拟接口请求
@time_counter
def request_api(url: str, timeout: int = 5) -> dict:
"""模拟接口请求函数"""
time.sleep(0.5)
return {"code": 200, "msg": "请求成功", "data": url}
# 调用
request_api("https://example.com/api")
# 查看元信息
print("函数名称:", request_api.__name__)
print("函数文档:", request_api.__doc__)
代码说明:
- 使用
try...finally确保无论函数是否异常,都能统计耗时; - 装饰器可复用于所有需要耗时统计的函数,无侵入、无元信息丢失。
5.2 权限校验装饰器
在后端服务、管理系统中,权限校验是核心功能,使用 wrapt 可编写稳定的权限校验装饰器。
import wrapt
# 模拟用户权限数据
user_info = {
"username": "admin",
"is_admin": True
}
# 权限校验装饰器
def admin_required(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
if not user_info.get("is_admin"):
raise PermissionError("无管理员权限,禁止访问")
print("权限校验通过")
return wrapped(*args, **kwargs)
return wrapper(func)
# 管理员接口
@admin_required
def delete_user(user_id: int):
print(f"删除用户: {user_id}")
# 调用
delete_user(1001)
代码说明:
- 装饰器独立于业务逻辑,可随时添加或移除;
- 支持类方法、静态方法、普通函数,兼容性极强。
5.3 函数参数校验装饰器
在数据处理、脚本开发中,参数错误会导致程序崩溃,使用 wrapt 实现统一参数校验装饰器,提升代码健壮性。
import wrapt
# 参数校验装饰器
def param_check(func):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
# 校验位置参数
for index, arg in enumerate(args):
if not isinstance(arg, (int, str)):
raise TypeError(f"第 {index} 个参数类型错误")
# 校验关键字参数
for key, value in kwargs.items():
if value is None:
raise ValueError(f"参数 {key} 不能为空")
return wrapped(*args, **kwargs)
return wrapper(func)
@param_check
def process_data(a, b, c=None):
print(f"处理数据: {a}, {b}, {c}")
process_data(1, "test", c="data")
# process_data(None, [], c=None) # 触发参数异常
代码说明:
- 统一封装校验逻辑,避免每个函数重复编写判断代码;
- 包装后函数签名不变,不影响调用提示。
六、相关资源
- Pypi地址:https://pypi.org/project/wrapt/
- Github地址:https://github.com/GrahamDumpleton/wrapt
- 官方文档地址:https://wrapt.readthedocs.io/
关注我,每天分享一个实用的Python自动化工具。
