Python 异常简介


发布日期 : 2024-04-09 01:07:59 UTC

访问量: 223 次浏览

Python 异常:简介

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

在 Python 中引发异常

甚至可以使用自定义消息来补充该语句。 假设正在编写一个小型玩具程序, 该程序只期望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 异常并立即停止:

开发过程中的调试 assert

重新访问上一节 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 确实会失败, 那个么最好坚持引发异常。 然而,有时可能不希望程序在遇到异常时失败, 那么应该如何处理这些情况呢?

使用 tryexcept 块处理异常

在 Python 中, 可使用 tryandexcept 块来捕获和处理异常。 Python 将执行该 try 语句后面的代码作为程序的正常部分。 该语句后面的代码 except 是程序对前面 try 子句中的任何异常的响应:

使用 try 和 except 块处理异常

正如之前所见, 当语法正确的代码遇到错误时, 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 . 如果在 macOSWindows 计算机上运行此代码, 将得到以下输出:

$ 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 语句来指示程序仅在没有异常的情况下执行特定的代码块:

尝试成功后继续 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 遇到 RuntimeErrorexcept 上面的块中处理的内容, 它也会执行。在 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…exceptelse 并捕获可能的异常:

# ...

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

执行后清理 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/, 由小编整理编译。


本文链接 :Python 异常简介