我正在尝试使用Python进行操作,如果它以某种方式失败,我想重试最多10次。如果它以任何其他方式失败我希望它立即失败。在10次重试后,我希望所有失败都传播给调用者。
我无法以令人满意的方式编写流控制代码。这是我想要的行为(但不是风格!)的一个例子:
def run():
max_retries = 10
for retry_index in range(max_retries):
try:
result = run_operation()
except OneTypeOfError:
if retry_index < max_retries - 1:
continue
else:
raise
if result.another_type_of_error:
if retry_index < max_retries - 1:
continue
else:
raise AnotherTypeOfError()
try:
result.do_a_followup_operation()
except AThirdTypeOfError:
if retry_index < max_retries - 1:
continue
else:
raise
return result
raise Exception("We don't expect to end up here")
起初我以为我可以重构这个,所以重试逻辑与错误处理逻辑是分开的。问题是,例如,如果result.do_a_followup_operation()引发了OneTypeOfError,那么在这种情况下我根本不想重试。我只想在上面编码的特定情况下重试。
所以我想也许我可以将它重构为一个返回结果的函数,一个引发的异常(如果有的话),以及一个标志,指示它是否是一个可重试的异常。不知怎的,这对我来说不如上面那么优雅。
我想知道Python中是否有任何流控制模式可能对此有所帮助。
答案 0 :(得分:0)
编辑:啊我没有意识到你的代码表明你没有使用多个除了块,正如JETM所指出的那样
这是ExceptionHandling的快速入门:
try:
# do something
except OneTypeOfError as e:
# handle one type of error one way
print(e) # if you want to see the Exception raised but not raise it
except AnotherTypeOfError as e:
# handle another type of error another way
raise e('your own message')
except (ThirdTypeOfError, FourthTypeOfError) as e:
# handle error types 3 & 4 the same way
print(e) # if you want to see the Exception raised but not raise it
except: # DONT DO THIS!!!
'''
Catches all and any exceptions raised.
DONT DO THIS. Makes it hard to figure out what goes wrong.
'''
else:
# if the try block succeeds and no error is raisedm then do this.
finally:
'''
Whether the try block succeeds or fails and one of the except blocks is activated.
Once all those are done with, finally run this block.
This works even if your program crashed and so is great for cleaning up for example.
'''
我很久以前就这样做了,并且在一种异常中递归地调用了相同的函数而不是另一种异常。我还将重试索引和max_retries变量传递给函数,这意味着将它们作为参数添加。
另一种方法是将整个事物放在max_retries的for循环中,并在除了块之外的所有区域中添加一个中断,以便您不想重试。
最后,不是for循环,而是将整个事物放在while循环中,在except块中为一种类型的异常插入一个增量条件,并将while条件设置为false,除了block以用于其他异常。
答案 1 :(得分:0)
您可以使用特定的异常类和递归稍微干一点。顺着这些路线:
class Retry(Exception):
def __init__(self, e):
super(Exception, self).__init__()
self.e = e
def run(max_retries=10, exc=None):
if max_retries <= 0:
raise exc or Exception('Whatever')
try:
try:
result = run_operation()
except OneTypeOfError as e:
raise Retry(e)
if result.another_type_of_error:
raise Retry(AnotherTypeOfError())
try:
result.do_a_followup_operation()
except AThirdTypeOfError as e:
raise Retry(e)
except Retry as r:
return run(max_retries=max_retries-1, exc=r.e)
else:
return result
具有给定迭代次数的迭代解决方案在语义上似乎有问题。毕竟,你希望整个事情成功,重试是一个后备。并且重试失败听起来像是一个基本案例。
答案 2 :(得分:0)
如果我理解正确,你可以通过改变两件事来做到:
将tries_left
从10开始计数而不是retry_index
从0开始计数更自然地读取并让你利用这些正数是真的。
如果您更改(或包装)run_operation()
,如果AnotherTypeOfError
为真,它就会引发result.another_error
,您可以合并前两个except
块
在else
之后省略raise
(或continue
之后,如果您选择测试if tries_left
),可以选择使代码更加密集 - 控件无论如何,流量在这一点被转移 - 并且通过在没有if
的情况下将一条简单的声明放在与else
相同的行上。
for tries_left in range(10, -1, -1):
try:
result = run_operation()
except OneTypeOfError, AnotherTypeOfError:
if not tries_left: raise
continue
try:
result.do_a_followup_operation()
except AThirdTypeOfError:
if not tries_left: raise
continue
return result