当前位置: 首页 > news >正文

2.2 python中带参数的装饰器与类装饰器

2.2 带参数的装饰器与类装饰器

  • 2.2 带参数的装饰器与类装饰器
      • 2.2 带参数的装饰器与类装饰器
        • 带参数的装饰器:可定制的函数包装器
        • 类装饰器:面向对象的装饰方式
        • 带参数的类装饰器
        • 装饰器组合与执行顺序
        • 最佳实践与注意事项

2.2 带参数的装饰器与类装饰器

2.2 带参数的装饰器与类装饰器

在上一节中,我们掌握了基础装饰器的原理和应用。现在,让我们深入探讨两个更强大的变体:带参数的装饰器和类装饰器。它们将装饰器的灵活性和表达能力提升到了新的高度。

带参数的装饰器:可定制的函数包装器

想象一下,基础装饰器就像一个标准化的包装盒,无论什么产品都用同一种方式包装。而带参数的装饰器则像一个智能包装机——你可以通过调节参数来改变包装的材料、颜色或样式,使其适应不同的需求。

带参数的装饰器本质上是一个三层嵌套函数的结构:

  1. 最外层函数接收装饰器自身的参数。
  2. 中间层函数接收被装饰的目标函数。
  3. 最内层函数执行实际的包装逻辑。

让我们通过一个实际的例子来理解这个结构。假设我们需要一个装饰器,可以按指定次数重复执行一个函数,并在每次执行前打印一条日志。

def repeat(num_times, verbose=False):"""一个带参数的装饰器,用于重复执行函数。Args:num_times (int): 重复执行的次数。verbose (bool): 如果为True,则打印每次执行的日志。"""# 这是装饰器工厂:接收参数,返回一个真正的装饰器def decorator(func):# 这是真正的装饰器:接收函数,返回包装后的函数@functools.wraps(func)def wrapper(*args, **kwargs):results = []for i in range(num_times):if verbose:print(f"第 {i+1} 次执行函数 {func.__name__}")result = func(*args, **kwargs)results.append(result)return resultsreturn wrapperreturn decorator# 使用装饰器
@repeat(num_times=3, verbose=True)
def greet(name):return f"Hello, {name}!"# 测试
print(greet("Alice"))

输出:

第 1 次执行函数 greet
第 2 次执行函数 greet
第 3 次执行函数 greet
['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']

执行流程解析:

  1. @repeat(num_times=3, verbose=True) 首先调用 repeat(3, True),它返回 decorator 函数。
  2. 然后这个 decorator 函数被应用到 greet 函数上,相当于执行 greet = decorator(greet)
  3. 最终,greet 指向了 wrapper 函数,这个包装器包含了重复执行和日志打印的逻辑。

带参数的装饰器在实际开发中极为有用,比如:

  • 重试机制@retry(max_attempts=5, delay=1)
  • 权限验证@require_role('admin')
  • 性能监控@timeit(unit='ms')
类装饰器:面向对象的装饰方式

如果说函数装饰器是过程式的包装,那么类装饰器则提供了面向对象的解决方案。类装饰器通过实现 __call__ 方法,让类的实例可以像函数一样被调用。

为什么需要类装饰器?

  1. 状态保持:类可以更自然地维护装饰器的内部状态。
  2. 更清晰的代码结构:对于复杂的装饰逻辑,类的继承和多态特性可以提供更好的代码组织。
  3. 配置灵活性:可以通过类属性和方法来动态配置装饰行为。

让我们创建一个记录函数调用历史的类装饰器:

class CallHistory:"""类装饰器,记录函数的调用历史。"""def __init__(self, func):self.func = funcself.history = []functools.update_wrapper(self, func)def __call__(self, *args, **kwargs):# 记录调用信息call_info = {'timestamp': time.time(),'args': args,'kwargs': kwargs}self.history.append(call_info)# 执行原始函数result = self.func(*args, **kwargs)# 记录返回值call_info['result'] = resultreturn resultdef get_history(self):"""获取调用历史"""return self.historydef clear_history(self):"""清空调用历史"""self.history.clear()@CallHistory
def calculate(x, y, operation='add'):if operation == 'add':return x + yelif operation == 'multiply':return x * y# 测试
calculate(2, 3)
calculate(5, 6, operation='multiply')
calculate(10, 20)print("调用历史:")
for i, record in enumerate(calculate.get_history(), 1):print(f"{i}. 参数: {record['args']}, 操作: {record.get('kwargs', {}).get('operation', 'add')}, "f"结果: {record['result']}")

类装饰器的优势:

  • 状态管理history 列表自然地维护了函数的调用记录。
  • 额外方法:我们可以提供 get_history()clear_history() 等方法来操作装饰器的状态。
  • 生命周期:类实例在装饰期间一直存在,适合需要长期维护状态的场景。
带参数的类装饰器

结合前两种技术,我们可以创建带参数的类装饰器,这提供了最大的灵活性:

class RateLimit:"""带参数的类装饰器,实现函数调用频率限制。"""def __init__(self, max_calls, period):# 存储装饰器参数self.max_calls = max_callsself.period = period  # 时间周期(秒)self.calls = []  # 记录调用时间def __call__(self, func):@functools.wraps(func)def wrapper(*args, **kwargs):now = time.time()# 清理过期的调用记录self.calls = [call_time for call_time in self.callsif now - call_time < self.period]# 检查是否超过限制if len(self.calls) >= self.max_calls:oldest_call = self.calls[0]wait_time = self.period - (now - oldest_call)raise RuntimeError(f"频率限制:请在 {wait_time:.2f} 秒后重试")# 记录本次调用self.calls.append(now)# 执行函数return func(*args, **kwargs)return wrapper# 使用:限制在10秒内最多调用2次
@RateLimit(max_calls=2, period=10)
def api_call(endpoint):print(f"调用 API: {endpoint}")return "success"# 测试
try:api_call("/users")api_call("/posts")api_call("/comments")  # 这会触发频率限制
except RuntimeError as e:print(f"错误: {e}")
装饰器组合与执行顺序

在实际项目中,你可能会在同一个函数上应用多个装饰器。理解它们的执行顺序至关重要:

@decorator_a
@decorator_b
@decorator_c
def my_function():pass

这等价于:

my_function = decorator_a(decorator_b(decorator_c(my_function)))

执行顺序是从下往上,但包装顺序是从上往下。也就是说,decorator_c 最先执行,但 decorator_a 是最外层的包装。

最佳实践与注意事项
  1. 使用 functools.wraps:始终使用 @functools.wraps 来保留被装饰函数的元数据。
  2. 保持装饰器通用性:使用 *args**kwargs 来确保装饰器能处理各种函数签名。
  3. 考虑性能:复杂的装饰器可能引入性能开销,在性能敏感的场景要谨慎使用。
  4. 文档化装饰器参数:为带参数的装饰器提供清晰的文档说明。

通过掌握带参数的装饰器和类装饰器,你已经具备了构建复杂、可配置的装饰器系统的能力。这些技术在框架开发、API 设计和系统架构中有着广泛的应用,是 Python 高级编程的重要工具。

http://www.dtcms.com/a/574301.html

相关文章:

  • Java程序导致CPU打满如何排查
  • 建设网站天河区.net网站开发流程
  • 海外设计网站建设网站怎样运营
  • 两学一做微网站交流扬州网站建设费用
  • 网站开发 xmind长春建站网站模板
  • WebView 调试工具全解析,解决“看不见的移动端问题”
  • 论坛网站建设流程wordpress 页面压缩
  • 如果做京东优惠卷的网站七台河新闻联播视频
  • 永久免费自助建站源代码一个企业网站ppt怎么做
  • 工商网站官网入口seo顾问是干什么
  • 国产化云桌面有哪些有实力的厂家?
  • MediaPipe+OpenCV的python实现交互式贪吃蛇小游戏
  • 解析网站dns软件外包公司如何接单
  • 汽车排放检测的 “模块化核心”:HORIBA OBS-ONE GS Unit 气体分析单元技术解析
  • 【每天一个AI小知识】:什么是联邦学习?
  • 安卓进阶——多媒体
  • Spring Boot3零基础教程,响应式编程的模型,笔记109
  • 解读IEC 60086-4 2025
  • 学做网站论坛 可以吗360建筑工程网
  • 济南旅游团购网站建设动态电子商务网站建设报告
  • 企业做网站用dedeCMS免费吗在线资源搜索引擎
  • 什么是离子注入的注入剂量?
  • 静态网站 挂马北京网站设计的公司价格
  • 厦门建设局网站商品房免费免费网站模板
  • 怎么做谷歌这样的网站刷赞网站推广免费链接
  • 5、foc控制系统——电流环设计
  • 代码随想录打卡day25:56.合并区间
  • 【C++】C++11新特性 (上)
  • vue3+ts element-plus动态Icon图标统一注册
  • 用户组管理指令大全