企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 6.1. 异常处理 * 6.1.1\. 为其他用途使用异常 与许多面向对象语言一样,Python 具有异常处理,通过使用 `try...except` 块来实现。 > 注意 > Python 使用 `try...except` 来处理异常,使用 `raise` 来引发异常。Java 和 C++ 使用 `try...catch` 来处理异常,使用 `throw` 来引发异常。 异常在 Python 中无处不在;实际上在标准 Python 库中的每个模块都使用了它们,并且 Python 自已会在许多不同的情况下引发它们。在整本书中你已经再三看到它们了。 * 使用不存在的字典关键字将引发 `KeyError` 异常。 * 搜索列表中不存在的值将引发 `ValueError` 异常。 * 调用不存在的方法将引发 `AttributeError` 异常。 * 引用不存在的变量将引发 `NameError` 异常。 * 未强制转换就混用数据类型将引发 `TypeError` 异常。 在这些情况下,我们都在简单地使用 Python IDE:一个错误发生了,异常被打印出来 (取决于你的 IDE,可能会有意地以一种刺眼的红色形式表示),这便是。这叫做_未处理_ 异常;当异常被引发时,没有代码来明确地关注和处理它,所以异常被传给置在 Python 中的缺省的处理,它会输出一些调试信息并且终止运行。在 IDE 中,这不是什么大事,但是如果发生在你真正的 Python 程序运行的时候,整个程序将会终止。 然而,一个异常不一定会引起程序的完全崩溃。当异常引发时,可以被_处理_ 掉。有时候一个异常实际是因为代码中的 bug (比如使用一个不存在的变量),但是许多时候,一个异常是可以预见的。如果你打开一个文件,它可能不存在。如果你连接一个数据库,它可能不可连接或没有访问所需的正确的安全证书。如果知道一行代码可能会引发异常,你应该使用一个 `try...except` 块来处理异常。 ## 例 6.1. 打开一个不存在的文件 ``` >>> fsock = open("/notthere", "r") Traceback (innermost last): File "<interactive input>", line 1, in ? IOError: [Errno 2] No such file or directory: '/notthere' >>> try: ... fsock = open("/notthere") ... except IOError: ... print "The file does not exist, exiting gracefully" ... print "This line will always print" The file does not exist, exiting gracefully This line will always print ``` | | | | --- | --- | | \[1\] | 使用内置 `open` 函数,我们可以试着打开一个文件来读取 (在下一节有关于 `open` 的更多内容)。但是那个文件不存在,所以这样就引发 `IOError` 异常。因为我们没有提供任何显式的对 `IOError` 异常的检查,Python 仅仅打印出某个关于发生了什么的调试信息,然后终止。 | | \[2\] | 我们试图打开同样不存在的文件,但是这次我们在一个 `try...except` 内来执行它。 | | \[3\] | 当 `open` 方法引发 `IOError` 异常时,我们已经准备好处理它了。`except IOError:` 行捕捉异常,接着执行我们自已的代码块,这个代码块在本例中只是打印出更令人愉快的错误信息。 | | \[4\] | 一旦异常被处理了,处理通常在 `try...except` 块之后的第一行继续进行。注意这一行将总是打印出来,无论异常是否发生。如果在你的根目录下确实有一个叫 `notthere` 的文件,对 `open` 的调用将成功,`except` 子句将忽略,并且最后一行仍将执行。 | 异常可能看上去不友好 (毕竟,如果你不捕捉异常,整个程序将崩溃),但是考虑一下别的方法。你该不会希望获得一个指向不存在的文件的对象吧?不管怎么样你都得检查它的有效性,而且如果你忘记了,你的程序将会在下面某个地方给出奇怪的错误,这样你将不得不追溯到源程序。我确信你做过这种事;这可并不有趣。使用异常,一发生错误,你就可以在问题的源头通过标准的方法来处理它们。 ## 6.1.1. 为其他用途使用异常 除了处理实际的错误条件之外,对于异常还有许多其它的用处。在标准 Python 库中一个普通的用法就是试着导入一个模块,然后检查是否它能使用。导入一个并不存在的模块将引发一个 `ImportError` 异常。你可以使用这种方法来定义多级别的功能――依靠在运行时哪个模块是有效的,或支持多种平台 (即平台特定代码被分离到不同的模块中)。 你也能通过创建一个从内置的 `Exception` 类继承的类定义你自己的异常,然后使用 `raise` 命令引发你的异常。如果你对此感兴趣,请看进一步阅读的部分。 下面的例子演示了如何使用异常支持特定平台功能。代码来自 `getpass` 模块,一个从用户获得口令的封装模块。获得口令在 UNIX、Windows 和 Mac OS 平台上的实现是不同的,但是这个代码封装了所有的不同之处。 ## 例 6.2. 支持特定平台功能 ``` # Bind the name getpass to the appropriate function try: import termios, TERMIOS except ImportError: try: import msvcrt except ImportError: try: from EasyDialogs import AskPassword except ImportError: getpass = default_getpass else: getpass = AskPassword else: getpass = win_getpass else: getpass = unix_getpass ``` | | | | --- | --- | | \[1\] | `termios` 是 UNIX 独有的一个模块,它提供了对于输入终端的底层控制。如果这个模块无效 (因为它不在你的系统上,或你的系统不支持它),则导入失败,Python 引发我们捕捉的 `ImportError` 异常。 | | \[2\] | OK,我们没有 `termios`,所以让我们试试 `msvcrt`,它是 Windows 独有的一个模块,可以提供在 Microsoft Visual C++ 运行服务中的许多有用的函数的一个API。如果导入失败,Python 会引发我们捕捉的 `ImportError` 异常。 | | \[3\] | 如果前两个不能工作,我们试着从 `EasyDialogs` 导入一个函数,它是 Mac OS 独有的一个模块,提供了各种各样类型的弹出对话框。再一次,如果导入失败,Python 会引发一个我们捕捉的 `ImportError` 异常。 | | \[4\] | 这些平台特定的模块没有一个有效 (有可能,因为 Python 已经移植到了许多不同的平台上了),所以我们需要回头使用一个缺省口令输入函数 (这个函数定义在 `getpass` 模块中的别的地方)。注意我们在这里所做的:我们将函数 `default_getpass` 赋给变量 `getpass`。如果你读了官方 `getpass` 文档,它会告诉你 `getpass` 模块定义了一个 `getpass` 函数。它是这样做的:通过绑定 `getpass` 到正确的函数来适应你的平台。然后当你调用 `getpass` 函数时,你实际上调用了平台特定的函数,是这段代码已经为你设置好的。你不需要知道或关心你的代码正运行在何种平台上;只要调用 `getpass`,则它总能正确处理。 | | \[5\] | 一个 `try...except` 块可以有一条 `else` 子句,就像 `if` 语句。如果在 `try` 块中没有异常引发,然后 `else` 子句被执行。在本例中,那就意味着如果 `from EasyDialogs import AskPassword` 导入可工作,所以我们应该绑定 `getpass` 到 `AskPassword` 函数。其它每个 `try...except` 块有着相似的 `else` 子句,当我们发现一个 `import` 可用时,就绑定 `getpass` 到适合的函数。 | ## 进一步阅读 * _Python Tutorial_ 讨论了异常,包括[定义和引发你自已的异常,以及一次处理多个异常](http://www.python.org/doc/current/tut/node10.html#SECTION0010400000000000000000)。 * _Python Library Reference_ 总结了[所有内置异常](http://www.python.org/doc/current/lib/module-exceptions.html)。 * _Python Library Reference_ 提供了 [getpass](http://www.python.org/doc/current/lib/module-getpass.html) 模块的文档。 * _Python Library Reference_ 提供了 [`traceback` 模块](http://www.python.org/doc/current/lib/module-traceback.html) 的文档,这个模块在异常引发之后,提供了底层的对异常属性的处理。 * _Python Reference Manual_ 讨论了 [`try...except` 块](http://www.python.org/doc/current/ref/try.html) 的内部工作方式。