Python 异常简介

Python 异常简介


发布日期: 2024-04-09 更新日期: 2024-04-09 编辑:xuzhiping 浏览次数: 567

标签:

摘要: Python 程序一旦遇到错误就会终止。在 Python 中,错误可以是语法错误或异常。在本教程中,您将了解什么是异常以及它与语法错误有何不同。之后将学习如何引发异常和做出断言,了解可以在 try… 块中使用的所有与异常相关的关键字,except 用于微调如...

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

相关推荐

关注公众号
获取免费资源

随机推荐


Copyright © Since 2014. 开源地理空间基金会中文分会 吉ICP备05002032号

Powered by TorCMS

OSGeo 中国中心 邮件列表

问题讨论 : 要订阅或者退订列表,请点击 订阅

发言 : 请写信给: osgeo-china@lists.osgeo.org