Python 异常处理指北

Posted crazy_itman

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 异常处理指北相关的知识,希望对你有一定的参考价值。

自 2003 年以来,Python 一直位列TIOBE 编程社区指数中最受欢迎的十大编程语言之列,截至 2022 年 12 月,它是最受欢迎的语言(领先于 C、C++ 和Java)。它在 2007 年、2010 年、2018 年和 2020 年被选为年度编程语言(因为“一年中收视率上升最快”)(截至 2020 年,唯一一种四次获得此殊荣的语言) .一项实证研究发现,对于涉及字符串操作和字典搜索的编程问题,脚本语言(如 Python)比传统语言(如 C 和 Java)效率更高,并确定内存消耗通常“优于 Java 而不是比 C 或 C++ 差得多”。​使用 Python 的大型组织包括维基百科、谷歌、雅虎!, CERN, NASA, Facebook, Amazon,Instagram, Spotify,和一些较小的实体,如ILM 和ITA。社交新闻网站Reddit主要是用 Python 编写的。

在使用 Python 编程时,通常会遇到两种类型的错误:语法错误和异常。

任何由无效语法、缩进或编程结构导致的错误通常被视为语法错误。当出现语法错误时,程序会在语法错误发生的地方崩溃。

异常是破坏计算机程序正常流程的异常情况。当异常发生时,我们应该处理这些异常以确保我们的程序不会突然崩溃。

异常处理是在计算机程序中实施检查以处理在我们的程序执行期间可能发生的错误(无论是否预期)的过程。(与大多数其他语言相比,Python 更倾向于“做事并请求原谅”的编程风格)

异常处理

与其他所有编程语言一样,Python 有一种处理程序执行期间发生的异常的方法。这意味着异常得到妥善处理:我们的 Python 程序不会崩溃。当语法正确的程序在运行时发生错误时,Python 使用try语句和except子句来捕获和处理异常。

由于大多数异常都是预期的,因此有必要在我们的 Python 程序中更有针对性或更具体地处理异常。特定的异常处理使程序的调试更加容易。

一些标准的 Python 异常

Python 有一个内置的异常列表,用于处理不同的异常。下面是一些内置的 Python 异常

序列号异常名称描述
1Exception所有用户定义的异常也应该从此类派生。
2ArithmeticError为各种算术错误引发的那些内置异常的基类。
3BufferError当无法执行缓冲区相关操作时引发。
4LookupError当用于映射或序列的键或索引无效时引发的异常的基类。
5AssertionError当断言语句失败时引发。
6AttributeError当属性引用或赋值失败时引发。
7ImportError当 import 语句在尝试加载模块时遇到问题时引发。
8IndexError当序列下标超出范围时引发。
9KeyError在现有键集中找不到映射(字典)键时引发。
10NameError当找不到本地或全局名称时引发。
11OverflowError当算术运算的结果太大而无法表示时引发。
12RuntimeError当检测到不属于任何其他类别的错误时引发。
13StopIteration由内置函数next()和迭代器的__next__()方法引发,以发出迭代器不再生成任何项的信号。
14SyntaxError当解析器遇到语法错误时引发。
15TypeError当操作或功能应用于不适当类型的对象时引发。
16ValueError当操作或函数接收到具有正确类型但值不合适的参数时引发。
17ZeroDivisionError当除法或模运算的第二个参数为零时引发。
18FileExistsError尝试创建已存在的文件或目录时引发。
19FileNotFoundError当请求文件或目录但不存在时引发。

使用 try 和 except 语句处理 Python 异常

tryexcept块用于 Python 中的异常处理。语法如下所示:

try:
    # some code that could cause an exception
except: 
    # some code to execute to handle exception

try块包含一段可以引发异常的代码,而该except块包含一些处理异常的代码。

下面我们举一个简单的例子:

print(3/0)

上面的代码将在程序终止时生成一条错误消息:

Traceback (most recent call last):
  File "/home/ini/Dev/Tutorial/sitepoint/exception.py", line 53, in <module>
    print(3/0)
ZeroDivisionError: division by zero

抛出异常的代码行可以这样处理:

try:
    print(3/0)
except ZeroDivisionError:
    print("Cannot divide number by Zero")

在上面的示例中,我们将第一个打印语句放在try块中。此块中的代码片段将引发异常,因为将数字除以零没有任何意义。该except块将捕获try块中引发的异常。tryexcept块通常一起用于处理 Python 中的异常。我们只是在控制台中打印了“Cannot divide number by Zero”,而不是之前生成的错误消息。

多个 Python 例外

在 Python中,有时会使用两个或多个except块来捕获不同的异常。多个 except 块帮助我们捕获特定的异常并在我们的程序中以不同的方式处理它们:

try:
    number = 'one'
    print(number + 1)
    print(block)
except NameError:
    print("Name is undefined here")
except TypeError:
    print("Can't concatenate string and int")

这是上面代码的输出:

Can't concatenate string and int

从上面的示例中,我们有两个except块指定我们要处理的异常类型:NameErrorTypeErrortry块中的第一个 print 语句抛出TypeError异常。Python 解释器检查每个except子句以找到合适的异常类,该异常类由第二个except块处理。“Can't concatenate string and int”打印在控制台中。

try块中的第二个打印语句被跳过,因为发生了异常。但是将执行最后一个except子句之后的任何代码:

try:
    number = 'one'
    print(number + 1)
    print(block)
except NameError:
    print("Name is undefined here")
except TypeError:
    print("Can't concatenate string and int")

for name in ['Chris', 'Kwame', 'Adwoa', 'Bolaji']:
    print(name, end=" ")

这是上面代码的输出:

Can't concatenate string and int
Chris Kwame Adwoa Bolaji

因为已经处理了异常,所以会执行try和except块之后的for循环。

一个通用的 Python except

我们可以有一个通用except块来捕获 Python 中的所有异常。通用except块可以与我们程序中的其他特定except块一起使用,以捕获未处理的异常。在所有特定块之后放置最通用的except子句是合乎逻辑的。这将在发生未处理的except异常时启动。我们用最后一个通用except块修改我们之前的示例:

names = ['Chris', 'Kwame', 'Adwoa', 'Bolaji']
try:
    print(names[6])
    number = 'one'
    print(number + 1)
    print(block)
except NameError:
    print("Name is undefined here")
except TypeError:
    print("Can't concatenate string and int")
except:
    print('Sorry an error occured somewhere!')

for name in names:
    print(name, end=" ")

这是上面代码的输出:

Sorry an error occured somewhere!
Chris Kwame Adwoa Bolaji

但是出现IndexError异常,因为它没有在任何指定的except块中处理。通用的except块处理了异常。执行通用块中的语句并执行for之后的循环,并在控制台中打印相应的输出。

raise语句

有时在我们的 Python 程序中,我们可能希望在某些不符合我们要求的条件下使用raise关键字引发异常。该raise语句由关键字本身、一个异常实例和一个可选参数组成。让我们看下面的代码片段:

def validate_password(password):
    if len(password) < 8:
        raise ValueError("Password characters less than 8")
    return password

try:
    user_password = input('Enter a password: ')
    validate_password(user_password)
except ValueError:
    print('Password should have more characters')

检查密码是否满足要求的raise ValueError长度,如果不满足条件则引发指定的异常。

else 子句

else子句可以添加到标准tryexcept块中。它位于except子句之后。该else子句包含我们希望在try语句未引发异常时执行的代码。让我们考虑以下代码:

try:
    number = int(input('Enter a number: '))
    if number % 2 != 0:
        raise ValueError
except ValueError:
    print("Number must be even")
else:
    square = number ** 2
    print(square)

当用户输入偶数时,我们的代码运行时不会引发异常。然后该else子句执行。我们现在在控制台中打印了偶数的平方。但是,else子句中可能出现的异常不会由前面的except块处理。

finally 子句

finally子句可以添加到tryexcept块中,并应在必要时使用。finally无论是否发生异常,子句中的代码始终执行。请参阅下面的代码片段:

try:
    with open('robots.txt', 'r', encoding='UTF-8') as f:
        first_line = f.readline()
except IOError: 
    print('File not found!')
else:
    upper_case = first_line.upper()
    print(upper_case.index('x'))
finally:
    print('The Python program ends here!')

这是上面代码的输出:

The Python program ends here!
Traceback (most recent call last):
  File "/home/ini/Dev/python/python_projects/extra.py", line 89, in <module>
    print(upper_case.index('x'))
ValueError: substring not found

在上面的示例中,我们试图读取try子句中robots.txt中的文件内容。由于没有引发异常,因此执行else子句中的代码。else子句中出现异常,因为x在变量中找不到子字符串upper_case。当没有 except 子句来处理异常时——如上面的代码片段所示——finally首先执行该子句,然后重新引发异常。

Python 文档是这样解释的:

except在执行orelse子句期间可能会发生异常。finally同样,在执行子句后再次引发异常。

异常组

ExceptionGroup在 Python 3.11 中可用。它提供了一种引发多个不相关异常的方法。处理ExceptionGroup的首选语法是except*. 异常组的语法如下所示:

ExceptionGroup(msg, excs)

初始化时,异常组有两个参数,msgexcs

  • msg: 描述性信息

  • excs:异常子组序列

让我们创建一个实例ExceptionGroup

eg = ExceptionGroup('group one', [NameError("name not defined"), TypeError("type mismatch")])

实例化异常组时,异常子组列表不能为空。我们将触发我们之前创建的异常组的一个实例:

raise eg

这是上面代码的输出:

+ Exception Group Traceback (most recent call last):
|   File "<string>", line 10, in <module>
  | ExceptionGroup: group one (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | NameError: name not defined
    +---------------- 2 ----------------
    | TypeError: type mismatch
    +------------------------------------

显示的回溯展示了异常组中包含的所有异常子组。

如前所述,ExceptionGroup最好用except*子句处理,因为它可以挑出异常组中的每个特定异常。通用except子句只会将异常组作为一个单元来处理,而不是具体的某一个。

请参阅下面的代码片段:

try:
    raise ExceptionGroup('exception group', [NameError("name not defined"), TypeError("type mismatch"), ValueError("invalid input")])
except* NameError as e:
    print("NameError handled here.")
except* TypeError as e:
    print("TypeError handled here.")
except* ValueError:
    print("ValueError handled here.")

这是该代码的输出:

NameError handled here.
TypeError handled here.
ValueError handled here.

每个except*子句处理异常组中的目标异常子组。任何未处理的子组都将重新引发异常。

Python 中的用户定义异常

内置异常很好,但我们的软件项目可能需要自定义异常。Python 允许我们创建用户定义的异常以满足我们的需要。Python 文档指出:

所有异常都必须是派生自BaseException的类的实例。

自定义异常是通过继承 Python的Exception类派生的。自定义异常的语法如下所示:

class CustomExceptionName(Exception):
    pass
try:
    pass
except CustomExceptionName:
    pass

让我们创建一个自定义异常并在以下示例的代码中使用它:

class GreaterThanTenError(Exception):
    pass

try:
    number = int(input("Enter a number: "))
    if number > 10:
        raise GreaterThanTenError
except GreaterThanTenError:
    print("Input greater than 10")
else:
    for i in range(number):
        print(i ** 2, end=" ")
finally:
    print()
    print("The Python program ends here")

在上面的示例中,我们创建了自己的类,其异常名称为GreaterThanTenException,它继承自Exception超类。我们在其中放置了一些可能会在try块中引发异常的代码,except块是我们的异常处理程序。如果没有抛出异常,则该else子句具有要执行的代码。最后,finally无论结果如何,该语句都会执行。

如果我们的 Python 程序的用户输入一个大于 10 的数字,则会引发 一个GreaterThanTenError错误,该except子句将处理异常,然后finally执行该子句中的打印语句。

结论

在本文中,我们了解了语法错误和异常之间的主要区别。我们还看到语法错误或异常会破坏我们程序的正常流程。

我们还了解到tryandexcept语句是 Python 中处理异常的标准语法。

在构建实际应用程序时异常处理很重要,因为您希望检测错误并适当地处理它们。Python 提供了一长串内置异常,这些异常在处理异常时非常有用。

以上是关于Python 异常处理指北的主要内容,如果未能解决你的问题,请参考以下文章

[常用工具] Python视频处理库VidGear使用指北

Python中的异常处理

Python 2.7 学习笔记 异常处理

Python语言之异常处理与测试

Python中的异常处理

Python基础编程236 ● 异常 ● 异常处理的基本格式