仅捕获Python运行时错误

1 投票
3 回答
6807 浏览
提问于 2025-04-17 13:11

我发现自己在处理异常时,没有指定异常类型,尤其是当我调用一些与系统库(比如shutil、http等)交互的代码时。这些库在系统状态不正常时(比如文件被锁定、网络不可用等)可能会抛出异常。

try:
    # call something
except:
    print("OK, so it went wrong.")

这让我有点烦恼,因为这样做会把SyntaxError和其他程序员错误引发的异常也一并捕获。我看到有建议说应该避免这种不具体的异常处理方式。

有没有什么约定,所有运行时错误都源自某个共同的异常基类,这样我可以在这里使用?或者有没有什么方法可以避免语法错误、模块导入失败等问题?即使是KeyError我也认为是个bug,因为如果我不确定某个键一定存在,我通常会用dict.get()来处理。

我不想一个个列出所有可能的异常类型,尤其是因为我调用了很多我无法控制的辅助代码。

更新:好的,回答让我意识到我问错问题了——我真正想知道的是,是否有Python的约定或明确的建议,要求库的作者使用特定的基类来定义他们的异常,以便将它们与更常见的SyntaxError等区分开来。

因为如果有这样的约定,作为库的使用者,我就可以对可能抛出的异常做出一般性的假设,尽管具体情况可能会有所不同。不知道这样说是否更清楚?

再次更新:Sven的回答让我终于明白,与其放弃并在顶层捕获所有异常,不如在更低层次处理和细化异常,这样顶层只需要关注来自下层的特定异常类型。

谢谢!

3 个回答

1

关于 RuntimeError,你可以看看这个链接:http://docs.python.org/library/exceptions.html#exceptions.RuntimeError

如果这不是你想要的(可能确实不是),可以查看那个页面上列出的异常。如果你对这些异常之间的关系感到困惑,我建议你花十分钟看看你感兴趣的异常的 __bases__ 属性,看看它们共享哪些基础类。需要注意的是,__bases__ 并不是覆盖整个层级的——你可能还需要查看父类的基础类。

5

可以查看这个列表,里面有Python内置异常的相关信息。听起来你主要想捕捉的是StandardError,不过这也包括了KeyError。还有其他一些基础类,比如ArithmeticErrorEnvironmentError,在某些情况下你可能会觉得它们很有用。

我发现第三方库通常会从Exception这个类派生出自己的自定义异常,具体可以参考这里的文档。程序员们也会在合适的情况下抛出标准的Python异常,比如TypeErrorValueError等。不幸的是,这样一来,就很难把库的错误和其他从Exception派生的错误分开处理。我希望Python能定义一个比如UserException的基础类。有些库确实会为它们的异常声明一个基础类,但你得知道这些类是什么,还要导入相应的模块,并明确捕捉它们。

当然,如果你想捕捉所有的异常,除了 KeyErrorIndexError,还有那些会让脚本停止运行的异常,你可以这样做:

try:
    doitnow()
except (StopIteration, GeneratorExit, KeyboardInterrupt, SystemExit):
    raise    # these stop the script
except (KeyError, IndexError):
    raise    # we don't want to handle these ones
except Exception as e:
    handleError(e)

不过,老实说,每次写这个都挺麻烦的。

6
  1. 尽量把 try 块写得小一些。

  2. 只捕捉你想处理的异常。查看你正在使用的函数的文档。

这样可以确保你考虑到可能出现的异常,并想好如果发生这些异常该怎么处理。如果发生了你从没想过的事情,你的异常处理代码可能根本无法正确处理这种情况,所以让异常继续传播可能更好。

你说过“讨厌必须列出每一种可能的异常类型”,但其实通常没那么麻烦。比如,打开一个文件?那就捕捉 IOError。处理一些库的代码?这些库通常有自己的异常层级,顶层异常是特定的——如果你想捕捉任何库相关的异常,就只需捕捉这个顶层异常。尽量具体,否则错误迟早会被忽视。

关于在Python中自定义异常的约定:它们 通常应该继承自 Exception。这也是大多数标准库中的自定义异常所继承的,所以至少你应该使用

except Exception:

而不是简单的 except 语句,因为后者也会捕捉到 KeyboardInterruptSystemExit。正如你自己所说,这样仍然会捕捉到很多你不想捕捉的异常。

撰写回答