大家好,欢迎来到IT知识分享网。
如果你已经用 Python 编程有一段时间,可能已经见过并使用过装饰器。虽然许多开发者都了解装饰器的基础用法,但收集一些有用且可复用的装饰器模式,可以显著提升你的代码质量和开发效率。
本文将介绍五种值得加入你的工具箱的装饰器模式。每种模式都附有示例实现和实际应用案例。让我们开始吧!

点击查看 GitHub 上的完整代码
什么是装饰器?
在深入讲解之前,让我们快速回顾一下什么是装饰器。归根结底,装饰器是一种用于在不修改原始函数(或类)源码的情况下,增强其功能的函数。装饰器的一般用法如下:
@decorator def function(): pass
这实际上等同于:
function = decorator(function)
现在,让我们进入精彩内容吧。
1. 记忆化(Memoization)
假设你有一个函数,需要根据输入做一些耗时的计算,比如复杂的数学运算、大型数据集的处理、API 调用等。每次都重复计算其实很浪费资源。
下面是一个简单的记忆化装饰器:
def memoize(func): """根据参数缓存函数的返回值。""" cache = {} def wrapper(*args, kwargs): # 创建唯一标识本次调用的 key key = str(args) + str(kwargs) if key not in cache: cache[key] = func(*args, kwargs) return cache[key] return wrapper
示例:斐波那契数列
假设你要递归计算斐波那契数列,可以这样记忆化:
@memoize def fibonacci(n): """计算第 n 个斐波那契数。""" if n <= 1: return n return fibonacci(n-1) + fibonacci(n-2) # 没有记忆化时会很慢 result = fibonacci(50) print(f"第 50 个斐波那契数是 {result}")
输出:
第 50 个斐波那契数是
没有记忆化时,fibonacci(50) 由于递归调用会非常慢;而加上记忆化后,几乎瞬间完成计算。
适用场景: 当你需要重复处理相同输入且函数结果不会变化时,适合采用这种模式。
2. 日志记录(Logging)
你不总是希望在函数里到处插入 print 或 debug 语句,尤其是想在多个函数中实现统一、规范的日志记录时。
这个装饰器可以记录函数调用的相关信息,非常适合调试和监控:
import logging import functools def log_calls(func=None, level=logging.INFO): """记录函数调用的参数和返回值。""" def decorator(func): @functools.wraps(func) def wrapper(*args, kwargs): args_str = ", ".join([str(a) for a in args]) kwargs_str = ", ".join([f"{k}={v}" for k, v in kwargs.items()]) all_args = f"{args_str}{', ' if args_str and kwargs_str else ''}{kwargs_str}" logging.log(level, f"调用 {func.__name__}({all_args})") result = func(*args, kwargs) logging.log(level, f"{func.__name__} 返回 {result}") return result return wrapper # 兼容 @log_calls 和 @log_calls(level=logging.DEBUG) 两种用法 if func is None: return decorator return decorator(func)
示例:日志记录函数调用
logging.basicConfig(level=logging.INFO) @log_calls def divide(a, b): return a / b # 这会自动记录调用和返回值 result = divide(10, 2) # 还可以自定义日志级别 @log_calls(level=logging.DEBUG) def multiply(a, b): return a * b result = multiply(5, 4)
适用场景: 适用于小型脚本、API 端点或批量作业调试时,统一记录函数调用信息。
3. 执行时间统计(Timing Execution)
有时你想快速了解函数运行所需的时间,尤其是涉及数据库、文件解析或模型训练等场景。
import time import functools def timeit(func): """统计并打印函数的执行时间。""" @functools.wraps(func) def wrapper(*args, kwargs): start_time = time.time() result = func(*args, kwargs) end_time = time.time() print(f"{func.__name__} 执行耗时 {end_time - start_time:.4f} 秒") return result return wrapper
示例:统计函数运行时间
@timeit def slow_function(): """一个故意运行缓慢的函数用于演示。""" total = 0 for i in range(): total += i return total result = slow_function() # 会打印运行耗时
输出:
slow_function 执行耗时 0.5370 秒
适用场景: 这种装饰器适用于对简单函数做基准测试,帮助你发现优化空间。
4. 自动重试(Retry on Failure)
在和外部服务交互或执行不稳定操作时,失败后自动重试可以让代码更健壮。
def retry(max_attempts=3, delay_seconds=1, backoff_factor=2, exceptions=(Exception,)): """在指定异常时自动重试函数。""" def decorator(func): @functools.wraps(func) def wrapper(*args, kwargs): attempts = 0 current_delay = delay_seconds while attempts < max_attempts: try: return func(*args, kwargs) except exceptions as e: attempts += 1 if attempts == max_attempts: logging.error(f"尝试 {attempts} 次后失败。最后的错误: {e}") raise logging.warning( f"第 {attempts} 次尝试失败,错误:{e}。{current_delay} 秒后重试..." ) time.sleep(current_delay) current_delay *= backoff_factor return wrapper return decorator
这个 retry 装饰器会在指定异常发生时自动重试函数,最多尝试 max_attempts 次。它实现了指数退避策略,即每次重试的间隔会倍增。你可以自定义最大重试次数、初始延迟、退避倍数和捕捉的异常类型。重试时会记录警告日志,最终失败时会记录错误并抛出最后一次异常。
示例:带重试机制的 API 查询
(确保已安装 requests 库)
import random import requests @retry(max_attempts=5, delay_seconds=1, exceptions=(requests.RequestException,)) def fetch_data(url): """带重试逻辑的 API 数据获取。""" response = requests.get(url, timeout=2) response.raise_for_status() # 4XX/5XX 响应抛出异常 return response.json() # 失败时会最多重试 5 次 try: data = fetch_data('https://api.example.com/data') print("数据获取成功!") except Exception as e: print(f"所有重试均失败:{e}")
适用场景: 适用于网络请求、不稳定 API 或任何 IO 密集型操作。
5. 输入验证(Input Validation)
你可以用装饰器自动验证函数输入,而无需让函数主体变得臃肿。
下面这个装饰器用于确保所有输入参数都是正整数:
def validate_positive_ints(func): def wrapper(*args): for arg in args: if not isinstance(arg, int) or arg <= 0: raise ValueError(f"{arg} 必须为正整数") return func(*args) return wrapper
示例:验证正整数输入
@validate_positive_ints def calculate_area(length, width): return length * width print(calculate_area(5, 10)) # 正常输出 50 print(calculate_area(-1, 10)) # 抛出 ValueError
输出:
50 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) in () 4 5 print(calculate_area(5, 10)) ----> 6 print(calculate_area(-1, 10)) in wrapper(*args) 3 for arg in args: 4 if not isinstance(arg, int) or arg <= 0: ----> 5 raise ValueError(f"{arg} 必须为正整数") 6 return func(*args) 7 return wrapper ValueError: -1 必须为正整数
适用场景: 适合用于数据处理管道和用户输入的参数校验。
总结
可以把装饰器看作是为函数“加装”额外行为的优雅工具。我们介绍的五种模式 —— 记忆化、日志记录、时间统计、自动重试和输入验证 —— 是我日常开发中的常用技巧。
将这些装饰器模式加入你的项目(或者更好地,将它们收集成一个实用工具模块),你会写出更简洁、可维护性更高的代码,也能节省大量重复劳动。
不妨思考一下,代码中哪些功能可以抽象成装饰器,并在全项目内统一应用。当你习惯用这种思路审视代码时,会发现装饰器的用武之地无处不在。
你最喜欢哪一种装饰器模式?你有没有自己写过、不可或缺的专属装饰器?欢迎一起交流分享!

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/178773.html