Python tryexcept块重新释放异常

2024-10-17 00:32:08 发布

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

不捕获内部函数的异常,而是在调用外部函数时捕获异常,这是一种不好的做法?让我们看两个例子:

备选方案a)

def foo(a, b):
    return a / b

def bar(a):
    return foo(a, 0)

try:
    bar(6)
except ZeroDivisionError:
    print("Error!")

赞成:清洁剂(在我看来)
反对:如果不查看foo,就无法判断bar引发了哪些异常

备选方案b)

def foo(a, b):
    return a / b

def bar(a):
    try:
        ret = foo(a, 0)
    except ZeroDivisionError:
        raise

    return ret

try:
    bar(6)
except ZeroDivisionError:
    print("Error!")

赞成:明确的
缺点:您正在编写一个try-except块来重新引发异常。在我看来,这也很难看

其他选择

我理解,如果您想处理异常或将多个异常组合在一起,选项b是唯一的选择。但是,如果您只想按原样重新提出一些特定的异常,该怎么办

我在政治公众人物中找不到任何能说明这一点的东西


Tags: 函数returnfoodef方案barerror例子
2条回答

处理错误

这是个坏习惯吗?依我看:不,不是。一般来说,这是一种良好的做法:

def foo(a, b):
    return a / b

def bar(a):
    return foo(a, 0)

try:
    bar(6)
except ZeroDivisionError:
    print("Error!")

原因很简单:处理错误的代码集中在主程序中的一个点上

在某些编程语言中,可能引发的异常必须在函数/方法级别声明。Python则不同:它是一种脚本语言,缺少这样的特性。当然,因此,有时您可能会意外地遇到异常,因为您可能不知道正在调用的其他代码可能会引发这样的异常。但这没什么大不了的:要解决这种情况,您的主程序中有try...except...

您可以通过以下方式弥补对可能异常的了解不足:

  • 记录可能提出的例外情况;如果编程语言本身没有帮助,您需要通过提供更广泛的文档来弥补这一缺陷
  • 进行广泛的测试

一般来说,遵循您的选项b)毫无意义。事情可能更加明确,但代码本身并不是这个明确信息的正确位置。相反,这些信息应该是函数/方法文档的一部分

因此,不是

def bar(a):
    try:
        ret = foo(a, 0)
    except ZeroDivisionError:
        raise

    return ret

。。。写:

def bar(a):
    """
    Might raise ZeroDivisionError
    """
    return foo(a, 0)

或者像我写的那样:

#
# @throws   ZeroDivisionError       Does not work with zeros.
#
def bar(a):
    return foo(a, 0)

(但您在文档中究竟依赖哪种语法是完全不同的问题,超出了本问题的范围。)

在某些情况下,捕获函数/方法中的异常是一种很好的做法。例如,如果您希望一个方法以任何方式成功,即使某些内部操作可能失败,也会出现这种情况。(例如,如果您试图读取一个文件,但如果该文件不存在,您希望使用默认数据。)但仅仅为了再次引发异常而捕获异常通常没有任何意义:目前我甚至无法想出这样做可能有用的情况(尽管可能有一些特殊情况)。如果您希望提供可能引发此类异常的信息,请不要依赖用户查看实现,而是查看函数/方法的文档

输出错误

无论如何,我不会按照您的方法只打印一条简单的错误消息:

try:
    bar(6)
except ZeroDivisionError:
    print("Error!")

提出合理的、人类可读的、简单的错误消息是相当费力的。我曾经这样做过,但是这种方法需要的代码量是巨大的。根据我的经验,最好是失败并打印出堆栈跟踪。使用此堆栈跟踪,通常任何人都可以很容易地找到错误的原因

不幸的是,Python在错误输出中没有提供非常可读的堆栈跟踪。为了弥补这一点,我实现了自己的错误输出处理(可重用为模块),甚至使用了颜色,但这是另一回事,可能也有点超出了这个问题的范围

如果您遵循Bob叔叔的干净代码手册,那么您应该始终将逻辑和错误处理分开。这将使选项a.)成为首选解决方案

我个人喜欢这样命名函数:

def _foo(a, b):
    return a / b

def try_foo(a, b):
    try:
        return _foo(a, b)
    except ZeroDivisionError:
        print('Error')


if __name__ == '__main__':
    try_foo(5, 0)        

相关问题 更多 >