展平嵌套的try / except子句

时间:2017-08-08 11:32:01

标签: python python-3.x

我想在同一个任务中尝试多种不同的方法,每次方法失败时都会捕获异常。我知道如果尝试失败将引发的异常(并且每次尝试可能会有所不同)。在最后一次尝试之后,我想优雅地放弃并继续前进。

我目前通过嵌套的try / except子句执行此操作:

try:
    first_approach()
except Exception1:
    try:
        second_approach()
    except Exception2:
        try:
            third_approach()
        except:
            give_up()
except Exception2:
    try:
        third_approach()
    except:
        give_up()

但这对我来说很糟糕,因为third_approach()会重复。我在the Python docs中看不到任何帮助。 那么我怎样才能展平这个丑陋的嵌套代码?

一个具体的例子

例如,假设我试图读取CSV文件列表,而不事先知道它们的编码。

某些CSV文件甚至可能是通过文件扩展名伪装成CSV的XLS文件。

因此,我想尝试一些不同的编码,如果这些编码都不起作用,请尝试将文件读作excel。

for f in files:
    try:
        read_csv(f, encoding='utf-8')
    except UnicodeDecodeError:
        try:
            read_csv(f, encoding='latin1')
        except NotCsvError:
            try:
                read_excel(f)
            except:
                give_up()
    except NotCsvError:
        try:
            read_excel(f)
        except:
            give_up()

3 个答案:

答案 0 :(得分:7)

您可以使用for / else循环遍历您的方法功能。

else子句仅在for语句未终止break时才会运行。

approaches = ((first_approach, [arg1, arg2, ...], {'kwarg1':kwarg1, 'kwarg2':kwarg2, ...}, (Approach1Exception1, Approach1Exception2, ...)),
              (second_approach, ..., ..., ...),
              (third_approach, ..., ..., ...), 
              ...)

for approach, args, kwargs, exceptions in approaches:
    try:
        approach(*args, **kwargs)
        break
    except exceptions: 
        pass
else:
    give_up()

答案 1 :(得分:2)

仍然嵌套,但DRYer:

for f in files:
    try:
        try:
            read_csv(f, encoding='utf-8')
        except UnicodeDecodeError:
            read_csv(f, encoding='latin1')
    except NotCsvError:
        try:
            read_excel(f)
        except:
            give_up()

在外部块中,首先尝试使用UTF-8,然后尝试使用Latin-1,如果它失败并出现UnicodeDecodeError。如果其中失败并带有NotCsvError,则控制权将传递给外部except子句。这利用了read_excel并不关心您之前尝试过哪种编码的事实。

答案 2 :(得分:0)

虽然我的解决方案仍然存在三个级别的嵌套,但尝试不同方法的逻辑已经趋于平缓:

ex = None
while True:
    try:
        if ex is None:
            first_approach()
        elif ex is Exception1:
            second_approach()
        elif ex is Exception2:
            third_approach()
        else:
            give_up()
        break
    except:
        ex = sys.exc_info()[0]

但请注意,此代码与您的版本不完全等效。例如,如果third_approach()引发Exception2,它将最终成为无限循环。