Python中如何在装饰器内部调用带该装饰器的函数实现重试
解决限流装饰器的重试问题
首先,你遇到的核心问题是:直接调用func(*args, **kwargs)时,执行的是未被装饰的原函数,完全绕开了限流检查逻辑。要实现带限流检查的重试,你需要重新触发整个wrapper的逻辑,而非直接调用原函数。
可行实现方案:循环重试(推荐)
用循环替代递归,避免栈溢出风险,同时可设置最大重试次数防止无限等待:
from functools import wraps import time import logging LOGGER = logging.getLogger(__name__) def rate_limiter(self, func: Callable): @wraps(func) def wrapper(*args, **kwargs): max_retries = 10 # 可根据需求调整最大重试次数 retry_delay = 2 # 每次重试等待的秒数 for attempt in range(max_retries): if is_request_allowed(): LOGGER.info('Request allowed') return func(*args, **kwargs) else: LOGGER.info(f'Request not allowed, retrying in {retry_delay}s (attempt {attempt+1}/{max_retries})') time.sleep(retry_delay) # 超过最大重试次数后的处理 LOGGER.error('Max retries exceeded, request failed') raise Exception('Rate limit exceeded after max retries') return wrapper
为什么直接调用func不行?
func是传入装饰器的原始函数对象,没有经过rate_limiter的包装,调用它不会触发限流检查逻辑,相当于绕过了你的限流规则。wrapper才是包含完整限流判断的包装函数,只有调用wrapper(*args, **kwargs)才能重新执行限流检查流程。
备选方案:递归重试(注意栈溢出)
如果想用递归实现,可直接在else分支调用wrapper本身,但必须设置递归深度限制,避免无限递归导致栈溢出:
def rate_limiter(self, func: Callable): @wraps(func) def wrapper(*args, **kwargs): if is_request_allowed(): LOGGER.info('Request allowed') return func(*args, **kwargs) else: LOGGER.info('Request not allowed, retrying...') time.sleep(2) return wrapper(*args, **kwargs) # 递归调用wrapper触发完整检查 return wrapper
⚠️ 注意:这种方式默认无重试次数限制,建议额外添加参数控制递归次数。
优化建议
可以将重试次数、延迟时间作为装饰器参数传入,让装饰器更灵活:
def rate_limiter(max_retries=10, retry_delay=2): def decorator(func: Callable): @wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_retries): if is_request_allowed(): LOGGER.info('Request allowed') return func(*args, **kwargs) LOGGER.info(f'Retry {attempt+1}/{max_retries} in {retry_delay}s...') time.sleep(retry_delay) LOGGER.error('Max retries reached') raise Exception('Rate limit exceeded') return wrapper return decorator # 使用方式示例 @rate_limiter(max_retries=5, retry_delay=3) def my_api_request(): # API请求逻辑 pass
内容的提问来源于stack exchange,提问作者Oscar
相关产品推荐
相关产品推荐

