当前位置:首页 > Python > 正文内容

Python 装饰器进阶:带参数装饰器与类装饰器实战

admin4周前 (03-22)Python37

装饰器(Decorator)是 Python 中最优雅的特性之一,它允许我们在不修改原函数代码的情况下,为函数添加额外功能。上一篇我们学习了基础装饰器,今天将深入探讨两个进阶主题:带参数的装饰器和类装饰器。

## 带参数的装饰器

基础装饰器只能接收被装饰的函数,但有时我们希望装饰器也能接收参数。例如,创建一个可配置的重试装饰器:

def retry(max_attempts=3, delay=1):\n    \"\"\"带参数的装饰器工厂函数\"\"\"\n    import time\n    \n    def decorator(func):\n        def wrapper(*args, **kwargs):\n            last_exception = None\n            for attempt in range(1, max_attempts   1):\n                try:\n                    return func(*args, **kwargs)\n                except Exception as e:\n                    last_exception = e\n                    if attempt < max_attempts:\n                        print(f\"第 {attempt} 次尝试失败, {delay}秒后重试...\")\n                        time.sleep(delay)\n            raise last_exception\n        return wrapper\n    return decorator\n

使用带参数装饰器时,括号是必需的:

@retry(max_attempts=5, delay=2)\ndef fetch_data(url):\n    import random\n    if random.random() < 0.7:\n        raise ConnectionError(\"网络连接失败\")\n    return \"数据获取成功\"\n\n# 调用函数\nprint(fetch_data(\"https://api.example.com\"))\n

## 类装饰器

装饰器不仅可以是函数,还可以是类。类装饰器使用 __call__ 方法来实现装饰功能:

class Timer:\n    \"\"\"类装饰器,用于测量函数执行时间\"\"\"\n    \n    def __init__(self, func):\n        self.func = func\n        \n    def __call__(self, *args, **kwargs):\n        import time\n        start_time = time.time()\n        result = self.func(*args, **kwargs)\n        end_time = time.time()\n        print(f\"{self.func.__name__} 执行时间: {end_time - start_time:.4f}秒\")\n        return result\n

使用类装饰器时,不需要括号:

@Timer\ndef fibonacci(n):\n    if n <= 1:\n        return n\n    return fibonacci(n-1)   fibonacci(n-2)\n\nprint(fibonacci(30))\n

## 带参数的类装饰器

如果需要带参数的类装饰器,需要一个两层嵌套结构:

class RateLimit:\n    \"\"\"带参数的类装饰器,实现调用频率限制\"\"\"\n    \n    def __init__(self, calls_per_second=1):\n        self.calls_per_second = calls_per_second\n        self.last_call_time = 0\n        \n    def __call__(self, func):\n        def wrapper(*args, **kwargs):\n            import time\n            current_time = time.time()\n            time_since_last_call = current_time - self.last_call_time\n            \n            if time_since_last_call < (1.0 / self.calls_per_second):\n                sleep_time = (1.0 / self.calls_per_second) - time_since_last_call\n                print(f\"达到频率限制,等待 {sleep_time:.2f}秒...\")\n                time.sleep(sleep_time)\n                \n            self.last_call_time = time.time()\n            return func(*args, **kwargs)\n        return wrapper\n

使用带参数的类装饰器:

@RateLimit(calls_per_second=2)\ndef api_request(request_id):\n    print(f\"处理请求 #{request_id}\")\n    \nfor i in range(5):\n    api_request(i 1)\n

## 装饰器叠加

多个装饰器可以叠加使用,执行顺序是从内到外(从下到上):

@retry(max_attempts=3)\n@Timer\ndef process_file(filepath):\n    import random\n    if random.random() < 0.5:\n        raise IOError(\"文件读取错误\")\n    print(f\"处理文件: {filepath}\")\n    return \"处理完成\"\n\nprocess_file(\"data.txt\")\n

## 实战案例:缓存装饰器

下面是一个实用的缓存装饰器,可以缓存函数结果避免重复计算:

def cache(max_size=128):\n    \"\"\"带参数的缓存装饰器\"\"\"\n    from functools import lru_cache\n    \n    def decorator(func):\n        return lru_cache(maxsize=max_size)(func)\n    return decorator\n\n@cache(max_size=64)\ndef expensive_computation(n):\n    print(f\"计算 {n} 的平方...\")\n    return n ** 2\n\n# 第一次调用会执行计算\nprint(expensive_computation(5))\n\n# 第二次调用从缓存读取\nprint(expensive_computation(5))\n

## 保留原函数元数据

装饰器会覆盖原函数的 __name____doc__ 等属性。使用 functools.wraps 可以保留这些元数据:

from functools import wraps\n\ndef log_function_call(func):\n    @wraps(func)\n    def wrapper(*args, **kwargs):\n        print(f\"调用函数: {func.__name__}\")\n        return func(*args, **kwargs)\n    return wrapper\n\n@log_function_call\ndef calculate_area(radius):\n    \"\"\"计算圆的面积\"\"\"\n    return 3.14159 * radius ** 2\n\nprint(calculate_area.__name__)  # 输出: calculate_area\nprint(calculate_area.__doc__)   # 输出: 计算圆的面积\n

## 总结

带参数装饰器通过"装饰器工厂函数"模式实现,需要三层嵌套结构。类装饰器使用 __call__ 魔术方法,更面向对象。两者都可以与基础装饰器自由组合,创造出强大的功能。

掌握这些进阶技巧后,你可以编写更灵活、更可复用的装饰器,为代码添加日志、缓存、权限验证、性能监控等功能,而无需修改业务逻辑代码。

相关文章

Python 装饰器完全指南:从原理到实战的 5 个核心场景

装饰器(Decorator)是 Python 中最具魅力的特性之一。它允许我们在不修改原函数代码的前提下,动态地添加功能。但很多开发者对装饰器的理解仅停留在在函数上面加个@符号的层面。今天,我们将从底...

Python 装饰器的实用技巧与高级用法

装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。让我们从最简单的例子开始,逐步深入到复杂的应用场景。首先,我们需要理解函数在 Python 中是一等公民。这意味...

深入理解 Python 装饰器:从基础到高级的完整指南

什么是装饰器?装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。装饰器的返回值通常也是一个函数对象。这种设计模式遵循了"开放封闭原则"——对扩展开放,...

Python 异步编程实战指南:从入门到精通

Python 异步编程实战指南:从入门到精通 简介 在现代 Python 开发中,异步编程已经成为构建高性能应用程序的核心技能。特别是在处理 I/O 密集型任务(如网络请求...

Python 类型提示实战指南:让代码更健壮

类型提示并不是强制执行的类型系统,而是一种可选的代码文档化工具。它通过注解函数参数和返回值的类型,帮助开发者更清晰地表达意图,同时让 IDE 和类型检查器(如 mypy)能够提前发现潜在的类型错误。...

Python 装饰器高级实战:从基础到精通的5个实用技巧

引言:为什么要深入掌握装饰器? 装饰器是 Python 中最优雅的元编程工具之一,它能在不修改原函数代码的情况下,动态地增加功能。很多开发者都知道如何使用 @timer 计时或 @cache 缓存,...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。