仅捕获Python运行时错误
我发现自己在处理异常时,没有指定异常类型,尤其是当我调用一些与系统库(比如shutil、http等)交互的代码时。这些库在系统状态不正常时(比如文件被锁定、网络不可用等)可能会抛出异常。
try:
# call something
except:
print("OK, so it went wrong.")
这让我有点烦恼,因为这样做会把SyntaxError
和其他程序员错误引发的异常也一并捕获。我看到有建议说应该避免这种不具体的异常处理方式。
有没有什么约定,所有运行时错误都源自某个共同的异常基类,这样我可以在这里使用?或者有没有什么方法可以避免语法错误、模块导入失败等问题?即使是KeyError
我也认为是个bug,因为如果我不确定某个键一定存在,我通常会用dict.get()来处理。
我不想一个个列出所有可能的异常类型,尤其是因为我调用了很多我无法控制的辅助代码。
更新:好的,回答让我意识到我问错问题了——我真正想知道的是,是否有Python的约定或明确的建议,要求库的作者使用特定的基类来定义他们的异常,以便将它们与更常见的SyntaxError
等区分开来。
因为如果有这样的约定,作为库的使用者,我就可以对可能抛出的异常做出一般性的假设,尽管具体情况可能会有所不同。不知道这样说是否更清楚?
再次更新:Sven的回答让我终于明白,与其放弃并在顶层捕获所有异常,不如在更低层次处理和细化异常,这样顶层只需要关注来自下层的特定异常类型。
谢谢!
3 个回答
关于 RuntimeError
,你可以看看这个链接:http://docs.python.org/library/exceptions.html#exceptions.RuntimeError
如果这不是你想要的(可能确实不是),可以查看那个页面上列出的异常。如果你对这些异常之间的关系感到困惑,我建议你花十分钟看看你感兴趣的异常的 __bases__
属性,看看它们共享哪些基础类。需要注意的是,__bases__
并不是覆盖整个层级的——你可能还需要查看父类的基础类。
可以查看这个列表,里面有Python内置异常的相关信息。听起来你主要想捕捉的是StandardError
,不过这也包括了KeyError
。还有其他一些基础类,比如ArithmeticError
和EnvironmentError
,在某些情况下你可能会觉得它们很有用。
我发现第三方库通常会从Exception
这个类派生出自己的自定义异常,具体可以参考这里的文档。程序员们也会在合适的情况下抛出标准的Python异常,比如TypeError
、ValueError
等。不幸的是,这样一来,就很难把库的错误和其他从Exception
派生的错误分开处理。我希望Python能定义一个比如UserException
的基础类。有些库确实会为它们的异常声明一个基础类,但你得知道这些类是什么,还要导入相应的模块,并明确捕捉它们。
当然,如果你想捕捉所有的异常,除了 KeyError
和IndexError
,还有那些会让脚本停止运行的异常,你可以这样做:
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)
不过,老实说,每次写这个都挺麻烦的。
尽量把
try
块写得小一些。只捕捉你想处理的异常。查看你正在使用的函数的文档。
这样可以确保你考虑到可能出现的异常,并想好如果发生这些异常该怎么处理。如果发生了你从没想过的事情,你的异常处理代码可能根本无法正确处理这种情况,所以让异常继续传播可能更好。
你说过“讨厌必须列出每一种可能的异常类型”,但其实通常没那么麻烦。比如,打开一个文件?那就捕捉 IOError
。处理一些库的代码?这些库通常有自己的异常层级,顶层异常是特定的——如果你想捕捉任何库相关的异常,就只需捕捉这个顶层异常。尽量具体,否则错误迟早会被忽视。
关于在Python中自定义异常的约定:它们 通常应该继承自 Exception
。这也是大多数标准库中的自定义异常所继承的,所以至少你应该使用
except Exception:
而不是简单的 except
语句,因为后者也会捕捉到 KeyboardInterrupt
和 SystemExit
。正如你自己所说,这样仍然会捕捉到很多你不想捕捉的异常。