什么是处理异常的首选方法?

4

当我在编写代码时遇到不确定的问题,我会尝试再次阅读 The Zen of Python。这一次,以下几行让我犹豫不决。

Errors should never pass silently.
Unless explicitly silenced.

目前的代码中,我有一些函数可能看起来像这样:

def add_v_1(a, b):
    return a + b 

所有涉及它们的调用都像这样:

c = add_v_1(7, [])

这样的代码异常会向上层传递并被捕获。

但是应该像这样吗?

add_v_1 可能会引发 TypeError 异常,我想从中恢复。 因此,可能调用函数的方式如下:

try:
    c = add_v_1(7, [])
except TypeError:
    print "Incorrect types!"

但是对于每个调用,我都需要进行异常处理。这看起来很繁琐。

所以,我可以这样做:

def add_v_2(a, b):
    try:
        return a + b
    except TypeError:
        print "Incorrect types!"

一个调用将会是:

c = add_v_2(7, [])

看起来更清晰。

似乎所有这些方法都遵循Python之禅,但其中哪一个是更好的选择呢?


1
我会说这取决于情况,在你的情况下,如果你在函数内部处理异常,你仍然必须在外部处理它,因为它将返回None。 - Hacketo
@Hacketo,你会如何处理当前的例子? - Viacheslav Kondratiuk
3个回答

3
关于你的例子,是否应该在函数内部捕获异常是有争议的。这样做会从函数中返回None,然后让调用代码检查函数的返回值并将None视为错误。它实际上用错误检查取代了异常处理,而前者通常更符合Python风格。
你应该考虑是否有意义捕获诸如尝试添加两个完全不同数据类型之类的错误。在这种情况下这样做似乎是编程错误,不是您在正常执行期间所期望的事情。
那么你能怎么处理这样的错误呢?在函数预期输出的领域内,似乎没有任何有用的值可以返回......除了None以表示无法产生结果。但是调用代码仍然必须执行某种错误检查......因此最好处理异常-至少只需要处理一次。
但这个例子是人为制造的,很难给出普遍的建议。在这种特定情况下,出于以上原因,我会允许异常向调用代码传播并在那里处理它。

2

当您能够从中恢复时,应处理异常。如果您尝试使用不兼容类型添加两个值,则无法从中恢复(没有进一步的上下文)。

假设您编写了一个IntegerWrapper类,您的函数“add_v_2”通常应尝试连接两个值。

def add_v_2(a, b):
    try:
        return a + b
    except TypeError as e:
        if isinstance(a, IntegerWrapper):
            return str(a) + b
        else:
            print("I dont know how to handle this type: " + type(a).__name__)
            # reraise the error so the caller can handle it
            raise

这将尝试从a是“错误类型”中恢复,但如果您知道如何从中恢复,则可以这样做。如果不知道(a是另一种类型),它会重新引发异常,以便知道如何处理此问题的人看到错误。
(当然,该实现包含错误,它并不是一个“好的实现”)

0

输入:

def add_v_2(a, b):
    try:
        return a + b
    except Exception as e:
        print str(e)
c = add_v_2(7, [])
print (c)

输出:

unsupported operand type(s) for +: 'int' and 'list'
None

@viakondratiuk - 在你的代码中,第二种方法是最好的,如果有任何情况需要再次执行相同的操作,则无需再次使用try catch。 - GThamizh
2
我不建议捕获“Exception”,因为它太过于泛泛。你应该捕获你期望并且能够处理的异常,例如在这种情况下你可以捕获“TypeError”。 - mhawke

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