第13章 异常

目录

13.1 异常概念

13.1.1 异常的概念

在生活中,使用计算机中的某个应用软件时,由于某种错误,可能会引发异常,如图所示。
第13章 异常
在程序中,当Python检测到一个错误时,解释器就会指出当前流程已无法继续执行下去,这时就出现了异常。例如,使用print()函数输出一个未定义的变量值,具体如下所示:
第13章 异常
在Python程序中,如果出现异常,而异常对象并未被捕获或处理,程序就会用自动回溯,返回一种错误信息,并终止执行,上述语句返回的错误信息如下:
第13章 异常
上述信息提示name变量名未定义,NameError为Python的内建异常类。异常是指因为程序出错而在正常控制流以外采取的行为,即异常是一个事件,该事件可能会在程序执行过程中发生并影响程序的正常执行。

13.1.2 异常类

Python为了区分不同的异常,其中内置了许多异常类,常见的异常类如表所示。
第13章 异常
第13章 异常
在表中,BaseException是异常的*类,但用户定义的类不能直接继承这个类,而是要继承Exception。Exception类是与应用相关异常的顶层基类,除了系统退出事件类(SystemExit、KeyboardInterrupt和GeneratorExit)之外,几乎所有用户定义的类都应该继承自这个类,而不是BaseException类。

13.2 捕获与处理异常

为了防止程序运行中遇到异常而意外终止,开发时应对可能出现的异常进行捕获并处理。Python程序使用try、except、else、finally这4个关键字来实现异常的捕获与处理。

13.2.1 try-except语句

try-except语句可以捕获异常并进行处理,其语法格式如下:
第13章 异常
当try语句块中某条语句出现异常时,程序就不再执行try语句块中后面的语句,而是直接执行except语句块,如例所示。
第13章 异常
需要注意的是,上例程序只能捕捉except后面的异常类,如果发生其他类型异常,程序依然会终止。例如,运行上例程序,输入ab再回车,则程序出现错误,如图所示。
第13章 异常
在图中,错误信息提示字符串类型不能转化为浮点型。为了保证程序正常运行,此时就需要捕获并处理多种异常,其语法格式如下:
第13章 异常
在程序中,虽然开发者可以编写处理多种异常的代码,但异常是防不胜防的,很有可能再出现其他异常,此时就需要捕获并处理所有可能发生的异常,其语法格式如下:
第13章 异常
如果程序发生了异常,但是没有找到匹配的异常类别,则执行不带任何匹配类型的except语句块。

13.2.2 使用as获取异常信息

为了区分不同的异常,可以使用as关键字来获取异常信息,其语法格式如下:
第13章 异常
如果程序需要获取多种异常信息,则可以使用如下语法格式:
第13章 异常
如果程序需要获取所有异常信息,则可以使用如下语法格式:
第13章 异常
所有的异常类都继承自BaseException类,因此上述语句可以获取所有异常信息。

13.2.3 try-except-else语句

try-except-else语句用于处理未捕获到异常的情形,其语法格式如下:
第13章 异常
如果try语句内出现了异常,则执行except语句块,否则执行else语句块。
接下来演示try-except-else语句的用法,如例所示。
第13章 异常

13.2.4 try-finally语句

在try-finally语句中,无论try语句块中是否发生异常,finally语句块中的代码都会执行,其语法格式如下:
第13章 异常
其中,finally语句块用于清理在try块中执行的操作,如释放其占有的资源(如文件对象、数据库连接、图形句柄等)。
另外,with-as语句可作为try-finally语句处理异常的替代,其语法格式如下:
第13章 异常
该语句用于定义一个有终止或清理行为的情况,如释放线程资源、文件、数据库连接等,在这些场合下使用with语句将使代码更加简洁。
在讲解文件打开与关闭时,本书使用的就是with-as语句。with后面的表达式的结果将生成一个支持环境管理协议的对象,该对象中定义了__enter__() 和__exit__()方法。在with内部的语句块执行之前调用__enter__()方法运行构造代码,如果在as后面指定了一个变量,则将返回值和这个变量名绑定。当with内部语句块执行结束后,自动调用__exit__()方法,同时执行必要的清理工作,不管执行过程中有无异常发生。
以上学习了try-except语句、try-except-else语句和try-finally语句,在实际开发中,经常需要将3种语句结合起来使用,具体如下所示:
第13章 异常
程序先执行try语句块,若try语句块中的某一语句执行时发生异常,则程序跳转到except语句,从上到下判断抛出的异常是否与except后面的异常类相匹配,并执行第一个匹配该异常的except后面的语句块。
若try语句块中发生了异常,但是没有找到匹配的异常类,则执行不带任何匹配类型的except语句块。
若没有发生任何异常,则程序在执行完try语句块后直接进入else语句块。
最后,无论程序是否发生异常,都会执行finally语句块。

13.3 触发异常

触发异常有两种情况:一种是程序执行中因为错误自动触发异常,另一种是显式地使用raise或assert语句手动触发异常。Python捕获与处理这两种异常的方式是相同的。本节主要介绍手动触发异常。

13.3.1 raise语句

raise语句可以手动触发异常,其使用方法有如下3种。

  1. 通过类名触发异常
    该方法只需指明异常类便可创建异常类的实例对象并触发异常,其语法格式如下:
    第13章 异常
    例如,手动触发语法错误异常,则可以使用以下语句:
    第13章 异常
    程序运行时,输出以下信息:
    第13章 异常
  2. 通过异常类的实例对象触发异常
    该方法只需指明异常类的实例对象便可触发异常,其语法格式如下:
    第13章 异常
    例如,手动触发除零导致的异常,则可以使用以下语句:
    第13章 异常
    程序运行时,输出以下信息:
    第13章 异常
    此外,该方法还可以指定异常信息,具体如下所示:
    第13章 异常
    程序运行时,输出以下信息:
    第13章 异常
  3. 重新触发异常
    raise语句还可以重新触发异常,具体如下所示:
    第13章 异常
    程序运行时,输出以下信息:
    第13章 异常
    可以看出,程序执行了except语句块中的代码,其中的raise语句会重新触发ZeroDivisionError异常,但此时异常对象并未被捕获或处理,因此程序终止运行。

13.3.2 assert语句

assert语句(又称断言)是有条件的触发异常,其语法格式如下:
第13章 异常
其中,当表达式为真时,不触发异常;当表达式为假时,触发AssertionError异常。若给定了参数部分,则在AssertionError后将参数部分作为异常信息的一部分给出。
assert语句的主要功能是帮助程序员调试程序,以保证程序运行的正确性,因此它一般在开发调试阶段使用。
接下来演示assert语句的用法,如例所示。
第13章 异常

13.4 自定义异常

Python中内置的异常类毕竟有限,用户有时须根据需求需设置其他异常,如学生成绩不能为负数、限定密码长度等。自定义异常类一般继承于Exception或其子类,其命名一般以Error或Exception为后缀,如例所示。
第13章 异常

13.5 回溯最后的异常

当触发异常时,Python可以回溯异常并提示许多信息,这可能会给程序员定位异常位置带来不便,因此,Python中可以使用sys模块中exc_info()函数来回溯最后一次异常信息,该函数返回一个元组(type, value/message, traceback),每个元素的具体含义如下所示:
type:异常的类型。
value/message:异常的信息或者参数。
traceback:包含调用栈信息的对象。
接下来演示该函数的用法,如例所示。
第13章 异常

13.6 小案例

计算test.txt文件中每行数字的总和与平均值,该文件可能不存在或为空,也可能某行不包含数字。程序中须捕获并处理可能发生的异常(不能使用with-as语句),具体实现如例所示。
第13章 异常

本章小结

本章主要介绍了异常,包括异常的概念、触发异常、捕获与处理异常、自定义异常、回溯最后的异常。学习完本章知识,须理解异常处理的作用,即它使程序能够正常执行,不至于程序因异常导致退出或崩溃。

上一篇:Python 中常用的保留字(关键字)详解


下一篇:24.异常处理