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

Python 装饰器高级实战:类装饰器与带参数装饰器完全指南

admin3周前 (03-23)Python24
# Python 装饰器高级实战:类装饰器与带参数装饰器完全指南 ## 简介 Python 装饰器(Decorator)是 Python 中最强大的特性之一,它可以在不修改原始函数代码的情况下,动态地为函数添加新功能。本教程将深入探讨装饰器的高级用法,包括类装饰器和带参数装饰器,并通过原创实例展示如何在实际项目中灵活运用这些技术。 ## 正文

装饰器本质上是一个接受函数作为参数的函数,它返回一个新的函数来替代原始函数。在基础用法中,装饰器常用于日志记录、性能测试、权限验证等场景。然而,当业务逻辑变得更复杂时,简单的函数装饰器可能无法满足需求,这时就需要使用更高级的装饰器形式。

让我们从一个实际需求开始:假设我们正在构建一个 Web API 框架,需要为多个端点添加统一的访问日志和性能监控功能。我们可以首先实现一个基础装饰器:

```python import time from functools import wraps from datetime import datetime def log_execution(func): @wraps(func) def wrapper(*args, **kwargs): start_time = time.time() print(f"[{datetime.now()}] 开始执行: {func.__name__}") result = func(*args, **kwargs) elapsed = time.time() - start_time print(f"[{datetime.now()}] 完成: {func.__name__}, 耗时: {elapsed:.3f}秒") return result return wrapper ```

这个基础装饰器可以很好地工作,但它的灵活性有限。现在让我们进入更高级的主题。

### 类装饰器

类装饰器使用类而非函数来实现装饰逻辑。它通过实现 `__call__` 方法,使得类的实例可以像函数一样被调用。类装饰器的优势在于可以维护装饰器自身的状态,实现更复杂的功能。

考虑一个场景:我们需要跟踪每个函数被调用的次数,并且能够在运行时查询这些统计信息。使用类装饰器可以轻松实现:

```python class CallCounter: def __init__(self, func): self.func = func self.call_count = 0 self.__name__ = func.__name__ # 保留原始函数名 def __call__(self, *args, **kwargs): self.call_count += 1 print(f"函数 {self.func.__name__} 第 {self.call_count} 次被调用") return self.func(*args, **kwargs) def get_stats(self): return { 'function_name': self.func.__name__, 'total_calls': self.call_count } ```

使用这个类装饰器非常直观:

```python @CallCounter def calculate_square(n): return n * n @CallCounter def calculate_cube(n): return n ** 3 # 测试调用 print(calculate_square(5)) # 函数 calculate_square 第 1 次被调用 print(calculate_cube(3)) # 函数 calculate_cube 第 1 次被调用 print(calculate_square(10)) # 函数 calculate_square 第 2 次被调用 # 查询统计信息 print(calculate_square.get_stats()) # {'function_name': 'calculate_square', 'total_calls': 2} print(calculate_cube.get_stats()) # {'function_name': 'calculate_cube', 'total_calls': 1} ```

类装饰器的另一个强大应用场景是实现缓存机制。下面是一个基于 LRU(最近最少使用)策略的缓存装饰器:

```python class LRUCache: def __init__(self, func, max_size=3): self.func = func self.max_size = max_size self.cache = {} # 参数到结果的映射 self.access_order = [] # 记录访问顺序 def __call__(self, *args, **kwargs): # 创建参数的缓存键 cache_key = (args, tuple(sorted(kwargs.items()))) if cache_key in self.cache: # 命中缓存,更新访问顺序 self.access_order.remove(cache_key) self.access_order.append(cache_key) print(f"缓存命中: {args}") return self.cache[cache_key] # 缓存未命中,调用原始函数 result = self.func(*args, **kwargs) # 如果缓存已满,移除最久未使用的项 if len(self.cache) >= self.max_size: oldest_key = self.access_order.pop(0) del self.cache[oldest_key] print(f"缓存淘汰: {oldest_key}") self.cache[cache_key] = result self.access_order.append(cache_key) print(f"缓存存储: {args}") return result def clear_cache(self): self.cache.clear() self.access_order.clear() print("缓存已清空") ```

这个缓存装饰器可以显著提高重复计算的性能:

```python @LRUCache(max_size=3) def expensive_computation(x): print(f"执行复杂计算: {x}") return x * x # 第一次调用,执行计算 print(expensive_computation(5)) # 执行复杂计算: 5 # 第二次调用,从缓存获取 print(expensive_computation(5)) # 缓存命中: (5,) # 调用不同的参数 print(expensive_computation(10)) # 执行复杂计算: 10 print(expensive_computation(15)) # 执行复杂计算: 15 # 缓存已满,再次调用新参数会淘汰最老的 print(expensive_computation(20)) # 执行复杂计算: 20, 缓存淘汰 ``` ### 带参数的装饰器

带参数的装饰器需要三层嵌套结构:装饰器工厂函数、真正的装饰器函数、以及包装函数。这种结构使得装饰器可以接受自定义参数,提供更高的灵活性。

让我们实现一个重试装饰器,它可以在函数执行失败时自动重试指定次数:

```python def retry(max_attempts=3, delay=1, exceptions=(Exception,)): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except exceptions as e: if attempt == max_attempts - 1: raise # 最后一次尝试失败,抛出异常 print(f"尝试 {attempt + 1}/{max_attempts} 失败: {e}") print(f"等待 {delay} 秒后重试...") time.sleep(delay) return None return wrapper return decorator ```

使用重试装饰器可以让不稳定的网络调用更加健壮:

```python import random @retry(max_attempts=3, delay=1, exceptions=(ValueError, ConnectionError)) def unstable_network_call(url): # 模拟不确定的网络响应 if random.random() < 0.7: raise ConnectionError("网络连接超时") return f"成功获取: {url}" # 测试 try: result = unstable_network_call("https://api.example.com/data") print(f"最终结果: {result}") except Exception as e: print(f"所有尝试失败: {e}") ```

带参数装饰器的另一个实用场景是实现权限检查。例如,我们可以创建一个装饰器来验证用户是否具有特定的权限:

```python def require_permission(*required_permissions): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # 假设第一个参数是用户对象 user = args[0] if args else kwargs.get('user') if not user: raise ValueError("用户信息缺失") user_permissions = user.get('permissions', []) missing_permissions = set(required_permissions) - set(user_permissions) if missing_permissions: raise PermissionError( f"权限不足。需要: {', '.join(missing_permissions)}" ) print(f"权限验证通过,执行: {func.__name__}") return func(*args, **kwargs) return wrapper return decorator ```

权限装饰器的使用示例:

```python @require_permission('read', 'write') def update_user_data(user, data): print(f"更新用户数据: {data}") return "更新成功" # 测试 admin_user = {'name': 'admin', 'permissions': ['read', 'write', 'delete']} regular_user = {'name': 'user', 'permissions': ['read']} print(update_user_data(admin_user, {'name': '张三', 'age': 25})) # 权限验证通过 # update_user_data(regular_user, {'name': '李四'}) # 抛出 PermissionError ``` ### 组合多个装饰器

在 Python 中,我们可以通过叠加多个装饰器来实现功能的组合。装饰器的执行顺序是从内向外,即最下方的装饰器最先执行。

```python @require_permission('read', 'write') @retry(max_attempts=2) @log_execution def critical_operation(user, data): print(f"执行关键操作: {data}") return "操作完成" ```

在这个例子中,执行顺序是: 1. `log_execution` 最先执行(记录开始时间) 2. `retry` 在其外层处理重试逻辑 3. `require_permission` 在最外层检查权限

这种组合方式让每个装饰器专注于单一职责,通过组合实现复杂的功能需求。

## 最佳实践建议

1. 使用 `functools.wraps` 保留原始函数的元数据,包括函数名、文档字符串等。

2. 保持装饰器的单一职责原则,一个装饰器只完成一个特定功能。

3. 为装饰器添加清晰的文档字符串,说明其用途和参数。

4. 在调试时,可以通过打印或日志记录来追踪装饰器的执行流程。

5. 对于复杂的装饰器逻辑,考虑使用类装饰器以更好地管理状态。

通过掌握类装饰器和带参数装饰器,你将能够编写出更加灵活和强大的 Python 代码,这些技术在实际项目开发中非常实用。

相关文章

[Python 教程] Python 网络请求与爬虫基础

Python 网络请求与爬虫基础 requests 是 Python 最常用的 HTTP 库。本文介绍网络请求和爬虫的基础知识。 一、基础请求 import requests # GET 请求 r...

Python 上下文管理器实战:从 with 语句到自定义资源管理

在 Python 编程中,上下文管理器(Context Manager)是一个强大但常被低估的特性。当你使用 open() 函数读取文件时,那个熟悉的 with 语句背后,正是上下文管理器在默默工作。...

Python 装饰器完全指南:从入门到实战

装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。想象一下,你有一个函数,你想在它执行前后添加一些额外的逻辑,比如日志记录、性能测试、权限验证等。装饰器就是为这种...

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

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

Python装饰器实战:从入门到精通的5个实用技巧

Python装饰器(Decorator)是Python中最强大的特性之一,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。从Web框架的日志记录到API的权限验证,装饰器无处不在。本文将通...

Python 装饰器实战与原理深度解析

在 Python 开发中,我们经常需要在多个函数中添加相同的功能,比如日志记录、性能计时、权限校验等。如果每个函数都重复编写这些代码,不仅效率低下,还容易出错。装饰器正是为了解决这类问题而诞生的。...

发表评论

访客

看不清,换一张

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