Python 装饰器的高级应用:从原理到实战
装饰器本质上是一个接受函数作为参数,并返回一个新函数的函数。这种设计模式源自函数式编程,在 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}")掌握装饰器的高级用法,将使你的代码更加优雅和可维护。通过合理使用装饰器,你可以将横切关注点(如日志、缓存、验证)从业务逻辑中分离出来,实现更加清晰的代码结构。在日常开发中,多思考哪些功能可以抽取为装饰器,这将是提升代码质量的有效途径。
