Python实用工具:decorator库详解——优雅实现装饰器,告别函数签名丢失

一、decorator库概述

decorator是Python中专门用于简化装饰器编写的第三方库,核心作用是帮助开发者快速创建标准、规范的装饰器,自动保留被装饰函数的元信息(如函数名、文档字符串、参数签名),解决原生装饰器容易丢失函数元数据的问题。其原理是通过封装包装逻辑,自动完成functools.wraps的底层操作,降低装饰器编写门槛。该库轻量无侵入,使用简单,适合所有Python版本,License为BSD许可。优点是编写简洁、兼容性强、自动保留函数信息,缺点是仅专注装饰器场景,功能单一。

二、decorator库安装方法

decorator作为PyPI上的标准第三方库,可通过pip命令快速安装,兼容Python 2.7至Python 3.12+所有主流版本,安装命令如下:

pip install decorator

安装完成后,可在Python交互环境中执行导入命令验证是否安装成功:

import decorator
print(decorator.__version__)

若正常输出版本号,说明安装无误,可直接在项目中使用。

三、decorator库基础使用详解

3.1 原生装饰器的痛点

在不使用decorator库时,Python原生装饰器需要手动使用functools.wraps修饰包装函数,否则被装饰后的函数会丢失原有的__name____doc__、参数签名等信息,导致调试、文档生成、反射操作出现异常。

原生装饰器示例:

import functools

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("执行函数前")
        result = func(*args, **kwargs)
        print("执行函数后")
        return result
    return wrapper

@my_decorator
def add(a, b):
    """两数相加函数"""
    return a + b

print(add.__name__)  # 输出 wrapper,而非 add
print(add.__doc__)   # 输出 None,丢失文档字符串

可以看到,被装饰后的函数add的名称和文档字符串都被替换成了包装函数wrapper的信息,这在实际开发中会带来诸多不便。

3.2 使用decorator编写基础装饰器

decorator库提供了decorator装饰器,只需将包装函数作为参数传入,即可自动保留被装饰函数的所有元信息,无需手动处理functools.wraps

基础使用示例:

from decorator import decorator

@decorator
def my_decorator(func, *args, **kwargs):
    print("函数执行前操作")
    result = func(*args, **kwargs)
    print("函数执行后操作")
    return result

@my_decorator
def add(a, b):
    """两数相加函数"""
    return a + b

# 调用函数
print(add(2, 3))
# 查看函数元信息
print(add.__name__)  # 输出 add,保留原函数名
print(add.__doc__)   # 输出 两数相加函数,保留文档字符串

代码说明:

  1. 导入decorator库中的核心装饰器函数;
  2. @decorator修饰自定义装饰器函数,函数参数固定为func, *args, **kwargs
  3. func代表被装饰的原函数,*args, **kwargs接收原函数的所有参数;
  4. 直接在装饰器函数中编写前置、后置逻辑,最后返回原函数的执行结果;
  5. 被装饰后的函数完整保留原有的名称、文档字符串、参数签名。

执行结果:

函数执行前操作
函数执行后操作
5
add
两数相加函数

3.3 带参数的装饰器实现

decorator库支持编写带参数的装饰器,只需在基础装饰器外层再包裹一层函数,用于接收自定义参数,使用方式更加灵活。

带参数装饰器示例:

from decorator import decorator

def log(level="info"):
    @decorator
    def log_decorator(func, *args, **kwargs):
        print(f"[{level.upper()}] 执行函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"[{level.upper()}] 函数执行完成")
        return result
    return log_decorator

# 使用默认参数
@log()
def multiply(a, b):
    """两数相乘函数"""
    return a * b

# 使用自定义参数
@log(level="error")
def divide(a, b):
    """两数相除函数"""
    return a / b

# 调用函数
print(multiply(4, 5))
print(divide(10, 2))

代码说明:

  1. 定义外层函数log,接收装饰器参数level,默认值为info
  2. 内层使用@decorator修饰实际的装饰器逻辑;
  3. 外层函数返回内层装饰器,实现参数传递;
  4. 调用装饰器时可传入自定义参数,控制装饰器的行为。

执行结果:

[INFO] 执行函数: multiply
[INFO] 函数执行完成
20
[ERROR] 执行函数: divide
[ERROR] 函数执行完成
5.0

3.4 装饰类方法

decorator库不仅支持装饰普通函数,还能完美兼容类的实例方法、类方法、静态方法,无需额外修改代码,适配面向对象开发场景。

装饰类方法示例:

from decorator import decorator

@decorator
def time_record(func, *args, **kwargs):
    import time
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print(f"函数 {func.__name__} 执行耗时: {end - start:.4f}s")
    return result

class Calculator:
    """计算器类"""

    @time_record
    def add(self, a, b):
        return a + b

    @classmethod
    @time_record
    def multiply(cls, a, b):
        return a * b

    @staticmethod
    @time_record
    def subtract(a, b):
        return a - b

# 创建实例并调用
calc = Calculator()
print(calc.add(10, 20))
print(Calculator.multiply(5, 6))
print(Calculator.subtract(30, 10))

代码说明:

  1. 定义通用的耗时统计装饰器time_record
  2. 分别装饰实例方法、类方法、静态方法;
  3. 装饰器无需区分方法类型,自动适配所有类方法场景;
  4. 执行后可正常输出函数耗时,且保留类方法的元信息。

四、decorator库高级用法

4.1 保留函数参数签名

在原生装饰器中,即使使用functools.wraps,也可能无法完整保留函数的参数签名,而decorator库可以完美解决这个问题,让inspect模块能正确获取函数参数信息。

参数签名保留示例:

from decorator import decorator
import inspect

@decorator
def simple_decorator(func, *args, **kwargs):
    return func(*args, **kwargs)

@simple_decorator
def user_info(name: str, age: int, gender: str = "male") -> str:
    """获取用户信息"""
    return f"姓名:{name}, 年龄:{age}, 性别:{gender}"

# 获取函数签名
sig = inspect.signature(user_info)
print("函数参数签名:", sig)
# 获取函数注解
print("函数参数注解:", user_info.__annotations__)

代码说明:

  1. 使用inspect.signature获取被装饰函数的参数签名;
  2. decorator库完整保留了参数名称、默认值、类型注解;
  3. 原生装饰器无法实现这种完整的参数签名保留效果。

执行结果:

函数参数签名: (name: str, age: int, gender: str = 'male') -> str
函数参数注解: {'name': <class 'str'>, 'age': <class 'int'>, 'gender': <class 'str'>, 'return': <class 'str'>}

4.2 叠加多个装饰器

decorator库支持多个装饰器叠加使用,执行顺序与原生装饰器一致,且所有装饰器都能正常保留函数元信息,无冲突问题。

多装饰器叠加示例:

from decorator import decorator

@decorator
def decorator1(func, *args, **kwargs):
    print("装饰器1前置")
    res = func(*args, **kwargs)
    print("装饰器1后置")
    return res

@decorator
def decorator2(func, *args, **kwargs):
    print("装饰器2前置")
    res = func(*args, **kwargs)
    print("装饰器2后置")
    return res

@decorator1
@decorator2
def test_func():
    print("执行原函数")

test_func()
print("函数名:", test_func.__name__)

执行结果:

装饰器1前置
装饰器2前置
执行原函数
装饰器2后置
装饰器1后置
函数名: test_func

4.3 无侵入式装饰现有函数

除了使用@语法糖装饰函数外,decorator库还支持无侵入式地为现有函数添加装饰器,无需修改原函数定义,适合对已有项目进行扩展。

无侵入式装饰示例:

from decorator import decorator

@decorator
def log_decorator(func, *args, **kwargs):
    print(f"调用函数: {func.__name__}")
    return func(*args, **kwargs)

# 定义原始函数
def hello(name):
    return f"Hello, {name}"

# 无侵入式添加装饰器
hello = log_decorator(hello)

# 调用装饰后的函数
print(hello("Python"))

执行结果:

调用函数: hello
Hello, Python

五、实际开发案例

5.1 接口请求重试装饰器

在网络请求、接口调用场景中,经常需要实现失败重试功能,使用decorator库可以快速编写通用的重试装饰器,适配所有请求函数。

from decorator import decorator
import time
import requests

def retry(max_retry=3, delay=1):
    @decorator
    def retry_decorator(func, *args, **kwargs):
        for i in range(max_retry):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                print(f"第{i+1}次执行失败,错误信息: {e},{delay}秒后重试")
                time.sleep(delay)
        raise Exception(f"重试{max_retry}次后仍执行失败")
    return retry_decorator

# 使用重试装饰器装饰请求函数
@retry(max_retry=2, delay=0.5)
def get_api_data(url):
    """请求接口数据"""
    response = requests.get(url, timeout=3)
    response.raise_for_status()
    return response.json()

# 调用函数
try:
    data = get_api_data("https://api.github.com")
    print("请求成功:", data)
except Exception as e:
    print("最终请求失败:", e)

5.2 权限校验装饰器

在Web开发、接口服务中,权限校验是常用功能,使用decorator库编写权限校验装饰器,可快速实现接口权限控制。

from decorator import decorator

user_permissions = ["read", "write"]

def require_permission(permission):
    @decorator
    def permission_decorator(func, *args, **kwargs):
        if permission not in user_permissions:
            raise PermissionError(f"缺少{permission}权限,无法执行操作")
        print(f"权限{permission}校验通过")
        return func(*args, **kwargs)
    return permission_decorator

@require_permission("write")
def create_data(data):
    """创建数据"""
    return f"创建数据成功: {data}"

@require_permission("delete")
def delete_data(data_id):
    """删除数据"""
    return f"删除数据{data_id}成功"

# 调用有权限的函数
print(create_data({"id": 1, "name": "test"}))

# 调用无权限的函数
try:
    delete_data(1)
except PermissionError as e:
    print(e)

相关资源

  • Pypi地址:https://pypi.org/project/decorator/
  • Github地址:https://github.com/micheles/decorator
  • 官方文档地址:https://decorator.readthedocs.io/

关注我,每天分享一个实用的Python自动化工具。