更多pythonic方式来处理嵌套尝试...除了块?

时间:2015-04-02 19:53:39

标签: python error-handling exception-handling

是否有更清洁或更pythonic的方法来执行以下操作?

try:
    error_prone_function(arg1)
except MyError:
    try:
        error_prone_function(arg2)
    except MyError:
        try:
            another_error_prone_function(arg3)
        except MyError:
            try:
                last_error_prone_function(arg4)
            except MyError:
                raise MyError("All backup parameters failed.")

基本上它是:如果尝试#1失败,请尝试#2。如果#2失败,请尝试#3。如果#3失败,请尝试#4。如果#4失败,......如果#n失败,那么最后会引发一些异常。

请注意,我不一定每次都调用相同的函数,也不是每次都使用相同的函数参数。我上午,期望每个函数都有相同的异常MyError

2 个答案:

答案 0 :(得分:10)

感谢John Kugelman的帖子here,我决定使用这个,利用for循环的鲜为人知的else子句来执行代码,当整个列表已经用完而没有{{ 1}}发生。

break

正如Daniel Roseman在下面评论的那样,请注意缩进,因为funcs_and_args = [(func1, "150mm"), (func1, "100mm", (func2, "50mm"), (func3, "50mm"), ] for func, arg in funcs_and_args : try: func(arg) # exit the loop on success break except MyError: # repeat the loop on failure continue else: # List exhausted without break, so there must have always been an Error raise MyError("Error text") 语句try条款。

答案 1 :(得分:2)

基于生成器的方法可能比数据驱动方法更灵活:

def attempts_generator():

#   try:
#       <the code you're attempting to run>
#   except Exception as e:
#       # failure
#       yield e.message
#   else:
#       # success
#       return

    try:
        print 'Attempt 1'
        raise Exception('Failed attempt 1')
    except Exception as e:
        yield e.message
    else:
        return

    try:
        print 'Attempt 2'
        # raise Exception('Failed attempt 2')
    except Exception as e:
        yield e.message
    else:
        return

    try:
        print 'Attempt 3'
        raise Exception('Failed attempt 3')
    except Exception as e:
        yield e.message
    else:
        return

    try:
        print 'Attempt 4'
        raise Exception('Failed attempt 4')
    except Exception as e:
        yield e.message
    else:
        return

    raise Exception('All attempts failed!')

attempts = attempts_generator()
for attempt in attempts:
    print attempt + ', retrying...'

print 'All good!'

我们的想法是构建一个通过重试循环逐步执行尝试块的生成器。

一旦生成器成功尝试,它将使用硬return停止自己的迭代。不成功的尝试会产生下一个回退的重试循环。否则,如果它用尽了,它最终会抛出一个无法恢复的错误。

这里的优点是try..excepts的内容可以是你想要的任何内容,而不仅仅是单个函数调用,如果由于某种原因它特别尴尬。生成器函数也可以在闭包中定义。

正如我在这里所做的那样,收益率也可以传回日志信息。

输出上面,顺便说一句,注意我让尝试2成功写成:

mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
All good!

如果您取消注释尝试2中的加注,那么它们都会失败,您会得到:

mbp:scratch geo$ python ./fallback.py
Attempt 1
Failed attempt 1, retrying...
Attempt 2
Failed attempt 2, retrying...
Attempt 3
Failed attempt 3, retrying...
Attempt 4
Failed attempt 4, retrying...
Traceback (most recent call last):
  File "./fallback.py", line 47, in <module>
    for attempt in attempts:
  File "./fallback.py", line 44, in attempts_generator
raise Exception('All attempts failed!')
Exception: All attempts failed!

编辑:

就您的伪代码而言,这看起来像:

def attempts_generator():
    try:
        error_prone_function(arg1)
    except MyError
        yield
    else:
        return

    try:
        error_prone_function(arg2)
    except MyError
        yield
    else:
        return

    try:
        another_error_prone_function(arg3)
    except MyError:
        yield
    else:
        return

    try:
        last_error_prone_function(arg4)
    except MyError:
        yield
    else:
        return

    raise MyError("All backup parameters failed.")

attempts = attempts_generator()
for attempt in attempts:
    pass

它会让任何异常,但MyError冒出来并停止整个事情。您还可以选择为每个块捕获不同的错误。