优雅的替代方法长异常链?

13

很多时候我发现自己写出来的东西看起来像这样:

try:
    procedure_a()
except WrongProcedureError:
    try:
        procedure_b()
    except WrongProcedureError:
        try:
            procedure_c()
        except WrongProcedureError:
            give_up()

这样做太丑陋了。有没有更优雅的方法来实现这种“试错知道哪个不出错”的逻辑呢?似乎这种情况经常会发生;我希望有一些我不知道的编程语言特性专门设计用于这种情况。


7
循环。你需要循环。循环的作用是重复执行相同的操作多次。 - Aran-Fey
6
我很想知道这里的实际情况......如果你经常这样做,那么也许代码可以重新架构,避免需要这样做?看起来可能很难推断出在任何给定情况下代码的作用。 - Michal Charemza
3个回答

22
你可以使用 for/else 结构来实现这个功能:
for proc in [procedure_a, procedure_b, procedure_c]:
    try:
        proc()
    except WrongProcedureError:
        continue
    else:
        break
else:
    give_up()

for循环的else子句仅在控制流程自然地从for子句底部退出时触发。如果您通过使用break语句中止循环(像在三个程序之一不抛出异常时那样),则不会触发该子句。


1
try:块中是否只需要写成proc(); break,而不必使用else:子句? - fferri
5
@fferri 当然可以!YMMV(你的情况可能会有所不同),但我喜欢使用 try/except/else。即使像 "break" 这样显而易见的东西,明确地表明这个 break 不是我期望失败的代码似乎也很好。 - Adam Smith

5

循环遍历过程。将循环包装到函数中。在成功的情况下提前从函数中返回:

def try_all():
    for procedure in [procedure_a, procedure_b, procedure_c]:
        try:
            procedure()
            return
        except WrongProcedureError:
            continue
    give_up()

3

其实你的方法没有问题。它使用了普通的语言特点,传达了清晰明确的信息。

其他方法也可以运行,但它们有点更加复杂,可能只适用于您要检查很多(不仅仅是3个)函数的情况。在这种情况下,我建议制作一个函数来隐藏那些复杂性。

import itertools
from contextlib import suppress

def call_functions(funcs, give_up_func):
    for func in itertools.chain(funcs, [give_up_func]):
        with suppress(WrongProcedureError):
            func()
            return  # only reached when func does not raise WrongProcedureError.

假设 `give_up_func` 不会引发该异常,且 `suppress` 需要Python 3(我认为是3.4)。
但是,当您实际需要这个时,您的代码可能存在根本性问题。这似乎是某种“回退”操作,可能应该使用子类或额外参数或策略/工厂来解决,而不是嵌套的 `try` 和 `except`。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接