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

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

admin2周前 (03-22)Python23

装饰器(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 教程] Python 多线程编程指南

Python 多线程编程指南 Python 的 threading 模块提供多线程支持。本文介绍多线程编程的基础和实用技巧。 一、创建线程 import threading import time...

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

装饰器本质上是接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。让我们从基础开始,逐步深入到高级应用。首先,我们需要理解函数在 Python 中是一等公民。这意味着函数可以像其他对...

Python 装饰器的 5 个实用场景:从入门到精通

装饰器(Decorator)是 Python 中的"函数包装器",它允许我们在不修改原函数代码的前提下,动态地添加功能。很多初学者学完 @decorator 语法后就止步不前,但实际上装饰器在实际工程...

Python 装饰器进阶:从理解到实战

装饰器是 Python 中一个非常强大的特性,它允许你在不修改原函数代码的情况下,为函数添加额外的功能。很多开发者虽然用过装饰器,但对其底层原理和高级用法理解不深。本文将从基础出发,深入讲解装饰器的工...

深入理解 Python 上下文管理器:从基础到高级应用

Python 的 with 语句和上下文管理器是每个开发者都应该掌握的高级技巧,但很多初学者对它的理解仅仅停留在文件操作层面。本文将深入讲解上下文管理器的原理、多种实现方式,以及在实际开发中的高级应用...

深入理解 Python 装饰器与上下文管理器:从原理到实战

在 Python 开发中,装饰器和上下文管理器是两个非常强大的高级特性。它们能够让代码更加简洁、可读,并且在不修改原有代码逻辑的情况下增强功能。本文将从实际应用场景出发,深入探讨这两个重要概念。一、装...

发表评论

访客

看不清,换一张

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