Python: 重试三次函数直到全部失败
我正在使用 Python
2.7,遇到了以下情况。我想尝试调用一个函数三次。如果这三次都出错了,我会抛出最后一次的错误。如果其中有一次成功了,我就会停止尝试,立刻继续后面的操作。
这是我现在的代码:
output = None
error = None
for _e in range(3):
error = None
try:
print 'trial %d!' % (_e + 1)
output = trial_function()
except Exception as e:
error = e
if error is None:
break
if error is not None:
raise error
有没有更好的代码片段可以实现同样的功能呢?
4 个回答
1
我发现了一种很简单的重试方法。这里有一个叫做retry的模块。
首先,你需要安装这个模块,可以用下面的命令:
pip install retry
然后在你的代码里导入这个模块。
from retry import retry
在方法上方使用
@retry
这个装饰器。我们可以给这个装饰器传一些参数,比如tries
(重试次数)、delay
(每次重试的间隔时间)和Exception
(指定要重试的错误类型)。
示例
from retry import retry
@retry(AssertionError, tries=3, delay=2)
def retryfunc():
try:
ret = False
assert ret, "Failed"
except Exception as ex:
print(ex)
raise ex
- 上面的代码每次都会失败,但使用了重试装饰器后,它会在失败时重试3次,每次重试之间间隔2秒。而且这个重试只针对断言失败的情况,因为我们指定了错误类型为AssertionError,如果是其他类型的错误,函数就不会重试。
3
忽略调试输出和古老的Python语法,这段代码看起来不错。我唯一想改的就是把它放进一个函数里,这样你就可以直接返回trial_function()的结果了。另外,error = None
就变得不必要了,相关的检查也可以省略。如果循环结束了,error一定是被设置过的,所以你可以直接抛出它。如果你不想用函数,可以考虑在for循环中使用else
,并在得到第一个结果后直接跳出循环。
for i in range(3):
try:
result = foo()
break
except Exception as error:
pass
else:
raise error
use_somehow(result)
当然,建议在函数中使用装饰器的想法依然适用。你也可以在本地应用这个,毕竟装饰器的语法只是语法糖而已:
# retry from powerfj's answer below
rfoo = retry(3)(foo)
result = rfoo()
6
这里有一种可能的解决方法:
def attempt(func, times=3):
for _ in range(times):
try:
return func()
except Exception as err:
pass
raise err
下面是一个包含print
语句的示例:
>>> attempt(lambda: 1/0)
Attempt 1
Attempt 2
Attempt 3
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
attempt(lambda: 1/0)
File "<pyshell#17>", line 8, in attempt
raise err
ZeroDivisionError: integer division or modulo by zero
如果你在使用Python 3.x时遇到UnboundLocalError
错误,可以这样调整:
def attempt(func, times=3):
to_raise = None
for _ in range(times):
try:
return func()
except Exception as err:
to_raise = err
raise to_raise
这是因为try
语句结束时,err
会被清空;根据文档的说明:
当异常通过
as target
被赋值后,它会在except语句块结束时被清除。
7
使用装饰器
from functools import wraps
def retry(times):
def wrapper_fn(f):
@wraps(f)
def new_wrapper(*args,**kwargs):
for i in range(times):
try:
print 'try %s' % (i + 1)
return f(*args,**kwargs)
except Exception as e:
error = e
raise error
return new_wrapper
return wrapper_fn
@retry(3)
def foo():
return 1/0;
print foo()