摘要: Python 程序一旦遇到错误就会终止。在 Python 中,错误可以是语法错误或异常。在本教程中,您将了解什么是异常以及它与语法错误有何不同。之后将学习如何引发异常和做出断言,了解可以在 try… 块中使用的所有与异常相关的关键字,except 用于微调如...
Python 程序一旦遇到错误就会终止。在 Python 中,错误可以是语法错误或异常。在本教程中,您将了解什么是异常以及它与语法错误有何不同。之后将学习如何引发异常和做出断言,了解可以在 try…
块中使用的所有与异常相关的关键字,except
用于微调如何处理 Python 异常。
学习如何:
- 在 Python 中引发异常
raise
- 调试并测试代码
assert
try
使用and
处理异常except
else
使用和微调异常处理finally
通过演练处理与平台相关的异常的实际示例,将了解这些关键字。最后还将学习如何创建自己的自定义 Python 异常。
了解异常和语法错误
当解析器检测到不正确的语句时,就会出现语法错误。观察以下示例:
Python 回溯
>>> print(0 / 0))
File "<stdin>", line 1
print(0 / 0))
^
SyntaxError: unmatched ')'
箭头指示解析器在何处遇到语法错误。此外,错误消息还提示出了什么问题。在此示例中,括号过多。删除它并再次运行代码:
Python
>>> print(0 / 0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
这次,遇到了异常错误。只要语法正确的 Python 代码导致错误,就会发生这种类型的错误。消息的最后一行指示您遇到的异常错误类型。
Python 不只是写异常错误,而是详细说明它遇到的异常错误类型。在本例中,它是一个ZeroDivisionError.Python
带有各种内置异常以及创建用户定义异常的可能性。
在 Python 中引发异常
在某些情况下,如果发生某种情况,可能希望通过引发异常来停止程序,可使用关键字来做到这一点 raise
:
甚至可以使用自定义消息来补充该语句。假设正在编写一个小型玩具程序,该程序只期望5以内的数字。当出现不需要的情况时,可以引发错误:
Python
number = 10
if number > 5:
raise Exception(f"The number should not exceed 5. ({number=})")
print(number)
程序停止并在终端或REPL上显示异常,提供有关问题所在的有用线索。请注意,最终的调用 print()
从未执行,因为 Python 在到达该代码行之前引发了异常。
使用 raise
关键字,可在 Python 中引发任何异常对象,并在发生不需要的情况时停止程序。
开发过程中的调试 assert
在继续使用Python中使用 try...except
块处理异常的最常见方法之前,将快速浏览一下与其他异常稍有不同的异常。
Python 提供了一种特定的异常类型,只应在开发过程中调试程序时使用该异常类型。这个例外是 AssertionError
.
它 AssertionError
很特别,因为不应该自己使用来引发它 raise
。
相反,可使用关键字来 assert
检查是否满足条件,并让 PythonAssertionError
在不满足条件时引发 。
程序应该仅在某些条件满足时尝试运行。如果 Python 检查你的断言并发现条件是 True
,程序可以继续进行。如果条件为 False
,那么程序将引发 AssertionError
异常并立即停止:
重新访问上一节 low.py
中的小脚本。目前,当不满足特定条件时,会显式引发异常:
number = 1
if number > 5:
raise Exception(f"The number should not exceed 5. ({number=})")
print(number)
假设将为生产系统安全地处理此约束,可将此条件语句替换为断言,以便在开发过程中快速保留此健全性检查:
number = 1
assert (number < 5), f"The number should not exceed 5. ({number=})"
print(number)
如果 number
的程序中的如下5,则断言通过并且脚本继续执行下一行代码。但是,如果设置 number
的值高于5(例如),10则断言的结果将是 False
:
number = 10
assert (number < 5), f"The number should not exceed 5. ({number=})"
print(number)
在这种情况下,Python 会引发一个 AssertionError
包含传递的消息,并结束程序执行:
$ python low.py
Traceback (most recent call last):
File "./low.py", line 2, in <module>
assert (number < 5), f"The number should not exceed 5. ({number=})"
^^^^^^^^^^
AssertionError: The number should not exceed 5. (number=10)
在此示例中,引发 AssertionError
异常是程序要做的最后一件事。然后该程序将停止并且不会继续。断言后面的调用 print()
将不会执行。
当在开发过程中调试程序时,以这种方式使用断言会很有帮助,因为将断言添加到代码中可以非常快速且直接。
但是,不应该依赖断言来捕获生产中程序的关键运行条件。-O 这是因为当使用和 -OO 命令行选项在优化模式下运行 Python 时,Python 会全局禁用断言:
$ python -O low.py
10
在程序的本次运行中,使用了-O命令行选项,该选项删除了所有 assert
语句。因此,脚本一直运行到最后并显示了一个高得可怕的数字!
注意:或者也可以通过 PYTHONOPTIMIZE
环境变量禁用断言。
在生产中, Python 代码可能会使用这种优化模式运行,这意味着断言并不是处理生产代码中运行时错误的可靠方法。当调试代码时,它们可以是快速且有用的帮助者,但永远不应该使用断言来为程序设置关键约束。
如果在上述情况 low.py
下确实会失败,那么最好坚持引发异常。然而,有时可能不希望程序在遇到异常时失败,那么应该如何处理这些情况呢?number5
若当数字大于5时,low.py
确实会失败,那个么最好坚持引发异常。然而,有时可能不希望程序在遇到异常时失败,那么应该如何处理这些情况呢?
使用 try
和 except
块处理异常
在 Python 中,可使用 tryandexcept
块来捕获和处理异常。Python 将执行该 try
语句后面的代码作为程序的正常部分。该语句后面的代码 except
是程序对前面 try
子句中的任何异常的响应:
正如之前所见,当语法正确的代码遇到错误时,Python 将引发异常错误。如果不处理这个异常错误,程序就会崩溃。在该 except
子句中,可以确定程序应如何响应异常。
下面的函数可以帮助理解 tryandexcept
块:
def linux_interaction():
import sys
if "linux" not in sys.platform:
raise RuntimeError("Function can only run on Linux systems.")
print("Doing Linux things.")
只能 linux_interaction()
在 Linux
系统上运行。RuntimeError
如果在 Linux 以外的操作系统上调用 Python
,它将引发异常。
注意:选择正确的异常类型有时可能很棘手。Python 附带了许多分层相关的内置异常,因此如果您浏览文档,很可能会找到合适的异常。
Python 甚至将一些异常分组,例如应用于指示警告条件的警告,以及 Python 根据系统错误代码引发的操作系统异常。
如果仍然没有找到合适的异常,那么可创建自定义异常。try
可以通过添加以下代码来赋予函数 a
:
# ...
try:
linux_interaction()
except:
pass
在这里处理错误的方法是分发一个 pass
.
如果在 macOS
或 Windows
计算机上运行此代码,将得到以下输出:
$ python linux_interaction.py
没有得到任何回应。这里的好处是程序没有崩溃。但是让发生的异常默默地过去是不好的做法,至少应该始终了解并记录运行代码时是否发生某种类型的异常。
为此,可以更改 pass
为生成信息性消息的内容:
# ...
try:
linux_interaction()
except:
print("Linux function wasn't executed.")
现在,当在 macOS 或 Windows 计算机上执行此代码时,将看到 except
块中的消息打印到控制台:
$ python linux_interaction.py
Linux function wasn't executed.
当运行此函数的程序中发生异常时,程序将继续运行并通知您函数调用不成功的事实。
没有看到 Python 由于函数调用而引发的错误类型,为了准确地了解出了什么问题,需要捕获该函数引发的错误。以下代码是捕获 RuntimeError
该消息并将其输出到屏幕的示例:
# ...
try:
linux_interaction()
except RuntimeError as error:
print(error)
print("The linux_interaction() function wasn't executed.")
在该 except
子句中,将分配 RuntimeError
给临时变量 error
(通常也称为临时 err
变量),以便可以访问缩进块中的异常对象。在本例中,将打印对象的字符串表示形式,它对应于附加到对象的错误消息。
在 macOS 或 Windows 计算机上运行此函数会输出以下内容:
$ python linux_interaction.py
Function can only run on Linux systems.
The linux_interaction() function wasn't executed.
第一条消息是 RuntimeError
,通知 Python 只能在 Linux 机器上执行该函数。第二条消息告知哪个函数未执行。
在上面的示例中,调用了自己编写的函数,当执行该函数时,捕获了 RuntimeError
异常并将其打印到屏幕上。这是打开文件并使用内置异常的另一个示例:
try:
with open("file.log") as file:
read_data = file.read()
except:
print("Couldn't open file.log")
如果 file.log
不存在,则该代码块将输出以下内容:
$ python open_file.py
Couldn't open file.log
这是一条信息性消息,程序仍将继续运行。然而 except
块当前将捕获任何异常,无论这是否与无法打开文件有关。如果看到此消息,即使 Python 引发了完全不相关的异常,也可能会陷入混乱。
因此,在处理异常时最好是具体化。
在 Python 文档中,可看到在这种情况下可以引发一些内置异常,例如:
当请求文件或目录但不存在时引发,对应于 errno ENOENT
。
当想要处理 Python 找不到所请求的文件的情况。要捕获此类异常并将其打印到屏幕上,可以使用以下代码:
try:
with open("file.log") as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
在这种情况下,如果 file.log
不存在,则输出将如下:
$ python open_file.py
[Errno 2] No such file or directory: 'file.log'
子句中可以有多个函数调用 try
,并预期捕获各种异常。这里需要注意的是,子句中的代码 try
一旦遇到任何异常就会停止。
警告:当使用裸 except
子句时,Python 会捕获继承自的任何异常 Exception-
这是大多数内置异常!捕获父类 Exception
会隐藏所有错误,甚至是那些根本没有预料到的错误。这就是为什么应该避免 except
在 Python 程序中使用裸子句。
相反,需要引用要捕获和处理的特定异常类。
看下面的代码。在这里,首先调用 linux_interaction()
然后尝试打开文件:
# ...
try:
linux_interaction()
with open("file.log") as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
except RuntimeError as error:
print(error)
print("Linux linux_interaction() function wasn't executed.")
如果在 macOS 或 Windows 计算机上运行此代码,将看到以下内容:
$ python linux_interaction.py
Function can only run on Linux systems.
Linux linux_interaction() function wasn't executed
在该 try
子句中,立即遇到了异常,并且没有到达尝试打开的部分 file.log
。现在看看如果文件不存在,在 Linux 机器上运行代码时会发生什么:
$ python linux_interaction.py
[Errno 2] No such file or directory: 'file.log'
请注意,如果像上面那样处理特定的异常,那么子句的顺序except并不重要。关键在于 Python 首先引发哪个异常。一旦 Python 引发异常,它就会从上到下检查 except 子句并执行它找到的第一个匹配的子句。
try
以下是有关使用 Python ...语句的关键要点 except
:
- Python 执行一个
try
子句,直到遇到第一个异常为止。 - 在
except
子句(异常处理程序)内,可确定程序如何响应异常。 - 可以预见多个异常并区分程序应如何响应它们。
- 避免使用裸
except
子句,因为它们可能隐藏意外的异常。
try
虽然与 一起使用 except
可能是会遇到的最常见的错误处理,但可以采取更多措施来微调程序对异常的响应。
尝试成功后继续 else
可以使用 Python 的 else
语句来指示程序仅在没有异常的情况下执行特定的代码块:
看下面的示例:
# ...
try:
linux_interaction()
except RuntimeError as error:
print(error)
else:
print("Doing even more Linux things.")
如果要在 Linux 系统上运行此代码,则输出将如下所示:
Shell
$ python linux_interaction.py
Doing Linux things.
Doing even more Linux things.
由于程序没有遇到任何异常,Python 执行了子句中的代码 else
。但是,如果在 macOS 或 Windows 系统上运行此代码,则会得到不同的输出:
Shell
$ python linux_interaction.py
Function can only run on Linux systems.
该 linux_interaction()
函数提出了一个 RuntimeError
。
已经处理了异常,因此程序不会崩溃,而是将异常消息打印到控制台。但是,嵌套在该子句下的代码 else
不会执行,因为 Python 在执行过程中遇到了异常。
请注意,这样构造代码与在 try…except
块的上下文之外添加对 print()
的调用不同:
Python
# ...
try:
linux_interaction()
except RuntimeError as error:
print(error)
print("Doing even more Linux things.")
如果没有将调用嵌套 print()
在该 else
子句下,那么即使 Python 遇到 RuntimeError
在 except
上面的块中处理的内容,它也会执行。在 Linux 系统上,输出是相同的,但在 macOS 或 Windows 上,将得到以下输出:
Shell
$ python linux_interaction.py
Function can only run on Linux systems.
Doing even more Linux things.
在 else
子句下嵌套代码可以确保只有当 Python 在执行 try…except
块时没有遇到任何异常时,它才会运行。
还可以在子句中创建一个嵌套的 try…except
块 else
并捕获可能的异常:
# ...
try:
linux_interaction()
except RuntimeError as error:
print(error)
else:
try:
with open("file.log") as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
如果要在 Linux 计算机上执行此代码,那么将得到以下结果:
Shell
$ python linux_interaction.py
Doing Linux things.
[Errno 2] No such file or directory: 'file.log'
从输出中,可以看到已 linux_interaction()
运行。因为 Python 没有遇到异常,所以它尝试打开 file.log
。该文件不存在,但没有让程序崩溃,而是捕获了异常 FileNotFoundError
并向控制台打印一条消息。
执行后清理 finally
想象一下,在执行代码后,总是必须执行某种操作来进行清理。Python 允许使用以下子句来执行此操作 finally
:
看一下下面的示例:
Python
# ...
try:
linux_interaction()
except RuntimeError as error:
print(error)
else:
try:
with open("file.log") as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
finally:
print("Cleaning up, irrespective of any exceptions.")
在此代码中,Python 将执行 finally
子句中的所有内容。如果在任何 try…except
块中的某个地方遇到异常,这并不重要。在 macOS 或 Windows 计算机上运行代码将输出以下内容:
Shell
$ python linux_interaction.py
Function can only run on Linux systems.
Cleaning up, irrespective of any exceptions.
finally
请注意,无论是否正在处理异常,块内的代码都会执行:
Python
# ...
try:
linux_interaction()
finally:
print("Cleaning up, irrespective of any exceptions.")
简化了上面的示例代码,但 linux_interaction()
仍然在 macOS 或 Windows 系统上引发异常。如果现在在 Linux 以外的操作系统上运行此代码,那么将获得以下输出:
$ python linux_interaction.py
Cleaning up, irrespective of any exceptions.
Traceback (most recent call last):
...
RuntimeError: Function can only run on Linux systems.
尽管 Python 引发了 RuntimeError,该子句中的代码 finally 仍然执行并将消息打印到控制台。
这可能很有帮助,因为如果脚本遇到未处理的异常,即使是 try…except
块之外的代码也不一定会执行。在这种情况下,程序将终止,try…except
块之后的代码将永远不会运行。但是,Python 仍将执行 finally 子句中的代码,这有助于确保文件句柄和数据库连接等资源得到正确清理。
在 Python 中创建自定义异常
由于 Python 提供了大量内置异常,在决定引发哪个异常时可能会找到合适的类型。然而,有时代码不符合模型。
Python 通过继承内置异常,可以轻松创建自定义异常类型。回想一下你的 linux_interaction()
函数:
linux_interaction.py
def linux_interaction():
import sys
if "linux" not in sys.platform:
raise RuntimeError("Function can only run on Linux systems.")
print("Doing Linux things.")
# ...
在这种情况下,使用 aRuntimeError
并不是一个糟糕的选择,但如果异常名称更具体一些,那就更好了。为此,可创建自定义异常:
linux_interaction.py
class PlatformException(Exception):
"""Incompatible platform."""
# ...
通常,可通过继承来在 Python 中创建自定义异常 Exception
,它也是大多数内置 Python 异常的基类,也可以从不同的异常继承,但选择 Exception
通常是最好的选择。
这确实是需要做的全部事情。在上面的代码片段中,还添加了一个描述异常类型并用作类主体的文档字符串。
注意: Python 需要在类主体中包含一些缩进代码。除了使用文档字符串之外,还可以使用 pass 省略号( ...)。但是,添加描述性文档字符串可以为自定义异常添加最大价值。
虽然可以自定义异常对象,但不需要这样做。通常,为自定义 Python 异常指定一个描述性名称就足够了,这样就会知道当 Python 在代码中引发此异常时发生了什么。
现在已经定义了自定义异常,可像任何其他 Python 异常一样引发它:
linux_interaction.py
class PlatformException(Exception):
"""Incompatible platform."""
def linux_interaction():
import sys
if "linux" not in sys.platform:
raise PlatformException("Function can only run on Linux systems.")
print("Doing Linux things.")
# ...
如果现在 linux_interaction()
在 macOS 或 Windows 上调用,那么将看到 Python 引发自定义异常:
$ python linux_interaction.py
Traceback (most recent call last):
...
PlatformException: Function can only run on Linux systems.
甚至可以使用自定义 PlatformException
作为其他自定义异常的父类,可以为用户可能运行代码的每个平台进行描述性命名。
结论
至此,已经熟悉了使用 Python 异常的基础知识。了解语法错误和异常之间的区别后,了解了在 Python 中引发、捕获和处理异常的各种方法,以及如何创建自己的自定义异常。
在本文中,可获得使用以下异常相关键字的经验:
raise
允许随时提出异常。assert
能够验证是否满足特定条件,如果不满足则引发异常。- 在该
try
子句中,所有语句都会执行,直到遇到异常。 except
允许捕获并处理 Python 在子句中遇到的一个或多个异常try
。else
允许您编写仅当 Python 在子句中没有遇到异常时才运行的代码段try
。finally
能够执行应始终运行的代码段, 无论 Python 是否遇到任何异常。
本文内容来源于网站:https://realpython.com/python-exceptions/,由小编整理编译。