我有一个循环,开头为for I,范围为(0,100)。正常情况下,它可以正常运行,但有时由于网络条件而出现故障。目前,我已经设置它,以便在失败时,它将继续在except子句中(继续到I的下一个数字)。
我是否可以将相同的数字重新分配给i,并再次运行失败的循环迭代?
我有一个循环,开头为for I,范围为(0,100)。正常情况下,它可以正常运行,但有时由于网络条件而出现故障。目前,我已经设置它,以便在失败时,它将继续在except子句中(继续到I的下一个数字)。
我是否可以将相同的数字重新分配给i,并再次运行失败的循环迭代?
当前回答
带超时的通用解决方案:
import time
def onerror_retry(exception, callback, timeout=2, timedelta=.1):
end_time = time.time() + timeout
while True:
try:
yield callback()
break
except exception:
if time.time() > end_time:
raise
elif timedelta > 0:
time.sleep(timedelta)
用法:
for retry in onerror_retry(SomeSpecificException, do_stuff):
retry()
其他回答
带超时的通用解决方案:
import time
def onerror_retry(exception, callback, timeout=2, timedelta=.1):
end_time = time.time() + timeout
while True:
try:
yield callback()
break
except exception:
if time.time() > end_time:
raise
elif timedelta > 0:
time.sleep(timedelta)
用法:
for retry in onerror_retry(SomeSpecificException, do_stuff):
retry()
你可以有一个专门的函数使用返回短路结果。比如这样:
def my_function_with_retries(..., max_retries=100):
for attempt in range(max_retries):
try:
return my_function(...)
except SomeSpecificException as error:
logging.warning(f"Retrying after failed execution: {error}")
raise SomeOtherException()
重新尝试的替代方案:坚韧和退缩(2020年更新)
重新尝试库是以前的方法,但遗憾的是,它有一些bug,自2016年以来就没有任何更新。其他的选择似乎是后退和坚韧。在写这篇文章的时候,tenacity有更多的GItHub星(2.3k vs 1.2k),并且最近更新了,因此我选择使用它。这里有一个例子:
from functools import partial
import random # producing random errors for this example
from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type
# Custom error type for this example
class CommunicationError(Exception):
pass
# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
retry,
stop=stop_after_delay(10), # max. 10 seconds wait.
wait=wait_fixed(0.4), # wait 400ms
retry=retry_if_exception_type(CommunicationError),
)()
@retry_on_communication_error
def do_something_unreliable(i):
if random.randint(1, 5) == 3:
print('Run#', i, 'Error occured. Retrying.')
raise CommunicationError()
for i in range(100):
do_something_unreliable(i)
上面的代码输出如下:
Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.
坚韧的更多设置。坚韧GitHub页面上列出了重试。
这里有一个快速装饰器来处理这个问题。7行,没有依赖关系。
def retry(exception=Exception, retries=3, delay=0):
def wrap(func):
for i in range(retries):
try:
return func()
except exception as e:
print(f'Retrying {func.__name__}: {i}/{retries}')
time.sleep(delay)
raise e
return wrap
@retry()
def do_something():
...
@retry(HTTPError, retries=100, delay=3)
def download_something():
...
可以添加的一个功能是扩展异常以处理多个异常(splat一个列表)。
在Python装饰器库中也有类似的东西。
请记住,它不测试异常,而是测试返回值。它会重新尝试,直到被修饰的函数返回True。
稍微修改一下版本就可以了。