用于有条件地重试操作的python流控制

2024-03-19 09:37:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试用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")

一开始我以为我可以重构它,这样重试逻辑就和错误处理逻辑分开了。问题是,例如,如果OneTypeOfError由结果:做后续手术(),在这种情况下,我根本不想重试。我只想在上面编码的特定情况下重试。在

如果我认为这是一个可重构的异常,那么这个异常是否可以返回到一个ryable函数中。不知怎么的,在我看来,这并没有上面说的那么优雅。在

我想知道Python中是否有任何流控制模式可以在这里有所帮助。在


Tags: runindexif方式resultoperationelsemax
3条回答

如果我理解正确的话,您可以通过改变两件事来完成以下操作:

  • 从10开始计算tries_left,而不是从0开始向上计算retry_index,这样读起来更自然,并且可以利用正数是真实的。

  • 如果您更改(或包装)run_operation()使其已经在AnotherTypeOfError为真时引发{},则可以合并前两个except块。

通过省略raise之后的else(或者如果您选择测试if tries_left,则在continue之后)可以使代码稍微更密集一点——控制流无论如何都在该点被转移,并且通过将一个简单的语句放在与没有else的空if同一行。在

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

编辑:啊,我没有意识到你的代码表明你没有使用多个except块,正如JETM所指出的那样

以下是异常处理的快速入门:

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.
    '''

很久以前我就这样做过一次,并且递归地在一种异常中调用同一个函数,而不是另一种异常。我还向函数传递了retry index和max\u retries变量,这意味着将它们作为参数添加。在

另一种方法是将整个过程放在一个for循环中,循环的max_retries,并在所有块中添加一个break,用于您不希望重试的异常。在

最后,代替for循环,您可以将整个事件放入while循环中,为一种类型的异常在except块中插入一个增量条件,并在except块中为其他异常设置while条件为false。在

您可以使用一个特定的异常类和递归来稍微简单一点。大致如下:

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

具有给定迭代次数的迭代解决方案在语义上似乎有问题。毕竟,你希望整件事都成功,而重试是一种退步。在我看来,重试次数不够听起来像是个基本情况。在

相关问题 更多 >