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

Python 装饰器的高级应用:从原理到实战

admin2个月前 (03-22)Python67

装饰器本质上是一个接受函数作为参数,并返回一个新函数的函数。这种设计模式源自函数式编程,在 Python 中通过 @ 语法得到优雅的体现。理解装饰器的核心在于掌握闭包的概念——内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。

让我们从一个基础的装饰器开始。下面的示例展示了一个计时装饰器,它可以测量函数执行时间:

import time\nimport functools\n\ndef timer(func):\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        start = time.perf_counter()\n        result = func(*args, **kwargs)\n        end = time.perf_counter()\n        print(f"{func.__name__} 执行耗时: {end - start:.4f} 秒")\n        return result\n    return wrapper\n\n@timer\ndef fibonacci(n):\n    if n <= 1:\n        return n\n    return fibonacci(n-1)   fibonacci(n-2)

在上面的代码中,functools.wraps 是一个关键工具。它复制了原始函数的元信息(如 __name__、__doc__),这对于调试和文档生成非常重要。没有它,装饰后的函数名称会变成 'wrapper',这会混淆日志和错误追踪。

装饰器的真正威力体现在它能够组合使用。你可以将多个装饰器堆叠在一起,每个装饰器负责一个特定的关注点。这种组合方式实现了关注点分离,使代码更加模块化。例如,我们可以同时使用计时和日志装饰器:

def logger(func):\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        print(f"[调用] {func.__name__} 参数: args={args}, kwargs={kwargs}")\n        result = func(*args, **kwargs)\n        print(f"[返回] {func.__name__} 结果: {result}")\n        return result\n    return wrapper\n\n@logger\n@timer\ndef data_processing(data):\n    time.sleep(0.1)  # 模拟处理\n    return [x * 2 for x in data]

类装饰器提供了另一种实现方式。当你需要维护装饰器的状态,或者想要更结构化的代码组织时,类装饰器是理想的选择。类装饰器通过实现 __call__ 方法来使实例可调用:

class CountCalls:\n    def __init__(self, func):\n        self.func = func\n        self.count = 0\n        functools.update_wrapper(self, func)\n\n    def __call__(self, *args, **kwargs):\n        self.count  = 1\n        print(f"{self.func.__name__} 被调用第 {self.count} 次")\n        return self.func(*args, **kwargs)\n\n@CountCalls\ndef calculate_total(prices):\n    return sum(prices)

带参数的装饰器是更高级的应用场景。当你想要根据不同的配置来定制装饰器的行为时,这种模式非常有用。实现带参数的装饰器需要额外的函数层嵌套:

def repeat(times):\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            results = []\n            for _ in range(times):\n                results.append(func(*args, **kwargs))\n            return results\n        return wrapper\n    return decorator\n\n@repeat(times=3)\ndef random_number():\n    import random\n    return random.randint(1, 100)

在实际应用中,装饰器常用于缓存昂贵的计算结果。下面的 memoize 装饰器实现了简单的记忆化功能,可以显著提升递归算法的性能:

class memoize:\n    def __init__(self, func):\n        self.func = func\n        self.cache = {}\n        functools.update_wrapper(self, func)\n\n    def __call__(self, *args):\n        if args not in self.cache:\n            self.cache[args] = self.func(*args)\n        return self.cache[args]\n\n@memoize\ndef optimized_fibonacci(n):\n    if n <= 1:\n        return n\n    return optimized_fibonacci(n-1)   optimized_fibonacci(n-2)

装饰器还可以用于实现重试机制,这在网络请求和外部 API 視用中特别有用。下面的 retry 装饰器会在函数失败时自动重试指定次数:

def retry(max_attempts=3, delay=1):\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            for attempt in range(max_attempts):\n                try:\n                    return func(*args, **kwargs)\n                except Exception as e:\n                    if attempt == max_attempts - 1:\n                        raise\n                    print(f"第 {attempt   1} 次尝试失败: {e}")\n                    time.sleep(delay)\n        return wrapper\n    return decorator\n\n@retry(max_attempts=3, delay=1)\ndef fetch_data():\n    import random\n    if random.random() > 0.7:\n        raise ConnectionError("网络连接失败")\n    return {"status": "success", "data": [1, 2, 3]}

最后,装饰器还可以用于权限验证和访问控制。这种模式在 Web 开发中非常常见:

def require_role(required_role):\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            user = kwargs.get('user') or args[0] if args else None\n            if not user or user.get('role') != required_role:\n                raise PermissionError(f"需要 {required_role} 权限")\n            return func(*args, **kwargs)\n        return wrapper\n    return decorator\n\n@require_role('admin')\ndef delete_user(user_id, user=None):\n    print(f"删除用户 {user_id}")

掌握装饰器的高级用法,将使你的代码更加优雅和可维护。通过合理使用装饰器,你可以将横切关注点(如日志、缓存、验证)从业务逻辑中分离出来,实现更加清晰的代码结构。在日常开发中,多思考哪些功能可以抽取为装饰器,这将是提升代码质量的有效途径。

相关文章

[Python 教程] OpenCV 绘图教程:图形与文本标注

OpenCV 绘图教程:图形与文本标注本文介绍如何在 OpenCV 中绘制各种图形和添加文本,用于图像标注和可视化。一、绘制基本图形1.1 创建画布import cv2 import&nb...

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

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

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

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

Python装饰器实战:从零到精通的5个经典场景

Python装饰器(Decorator)是一个非常强大且优雅的语言特性,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。本文将通过5个实战场景,带你深入理解装饰器的原理和应用。 一、装饰...

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

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

Python 数据处理三部曲:从清洗到可视化的实战指南

在现代数据驱动的工作场景中,无论是处理实验数据、分析用户行为,还是监控业务指标,高效的数据处理能力都是不可或缺的。Python 提供了一套完整的数据处理工具链,其中 NumPy、Pandas 和 Ma...

发表评论

访客

看不清,换一张

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