错误和异常处理

  • 程序的错误
  • 语法错误
  • 运行时错误
  • 逻辑错误
  • 异常处理
  • 异常处理概述
  • 内置的异常类
  • 引发异常
  • 捕获处理异常机制概述
  • Python虚拟机捕获处理异常
  • 使用try...except...else...finally语句捕获处理异常
  • 捕获异常的顺序
  • finally块和发生异常后的处理
  • 自定义异常类
  • 断言处理
  • 断言处理概述
  • assert语句和AssertionError类
  • 启用/禁用断言
  • 程序的基本调试方法
  • 语法错误的调试
  • 运行时错误的调试
  • 逻辑错误的调试



程序的编写和运行过程中,不可避免会产生错误bugs和异常exceptions,Python语言采用结构化的异常处理机制捕获和处理异常

程序的错误

Python程序的错误可以分为三种类型:语法错误运行时错误逻辑错误

语法错误

Python程序的语法错误是指其源代码中拼写语法错误,这些错误导致Python编译器无法把Python源代码转换为字节码,故也称之为编译器错误。程序中包含语法错误时,编译器将显示SyntaxError错误信息

通过分析编译抛出的运行时错误信息,仔细分析相关位置的代码,可以定位并修改程序错误。

运行时错误

Python程序的运行错误是在解释执行过程中产生的错误。例如,如果程序中没有导入相关模块,解释器将在运行时抛出NameError错误信息;如果程序中包含零除运算,解释器将在运行时抛出ZeroDivisionError错误信息;如果程序中试图打开不存在的文件,解释器将在运行时抛出FileNotFoundError错误信息

通过分析解释器抛出的运行时错误信息,仔细分析相关位置的代码,可以定位并修改程序错误

逻辑错误

Python程序的逻辑错误是程序可以执行(程序运行本身不能报错),但执行结果不正确,对于逻辑错误,Python解释器无能为力,需要读者根据结果来调试判断

异常处理

异常处理概述

Python语言采用结构化异常处理机制。在程序运行过程中,如果产生错误,则抛出异常;通过try语句来定义代码块,以运行可能抛出异常的代码;通过except语句,可以捕获特定的异常并执行相应的处理;通过finally语句,可以保证即使产生异常(处理失败),也可以在事后清理资源等。

def readfile():
	打开文件    #可能产生错误:文件不存在
	读取文件    #可能产生错误:无读取权限
	关闭文件

使用Python的结构化异常处理机制,其伪代码一般如下:

def read_file():
	try:
		打开文件                 #可能产生错误,文件不存在
		读取文件内容             #可能产生错误,无读取权限
		关闭文件
	except FileNotFoundError:   #捕获异常,无法打开文件
		#异常处理逻辑
	except PermissionError:     #捕获异常,无读取权限
		#异常处理逻辑

从上面伪代码可以看出,异常处理机制可以把错误处理和正常代码逻辑分开,从而可以更加高效错误处理,增加程序的可维护性。异常处理机制已经成为许多现代程序设计语言处理错误的标准模式。

内置的异常类

在程序运行的过程中,如果出现错误,Python解释器会创建一个异常对象,并抛出给系统运行时。即程序终止正常执行流程,转而执行异常处理程序。

在某种特殊条件下,代码中也可以创建一个异常对象,并通过raise语句,抛出给系统运行时

异常对象是异常类的对象实例。Python异常类均派生于BaseException,Python内置的异常类的层次结构如下图:

Python学习笔记:错误和异常处理_Python错误和异常处理


常见异常示例:

NameError           #尝试访问一个未申明的变量
SyntaxError         #语法错误
AttributeError      #访问未知对象属性
TypeError           #类型错误
ValueError          #数值错误
ZeroDivisionError   #零除错误
IndexError          #索引超出范围
KeyError            #字典关键字不存在

引发异常

大部分由程序而产生的错误和异常,一般由Python虚拟机自动抛出。另外,在程序中,如果判断某种错误情况,则可以创建相应的异常类和对象,并通过raise语句抛出

捕获处理异常机制概述

当程序中的某个方法抛出异常后,Python虚拟机通过调用堆栈查找相应的异常捕获程序,如果找到匹配的异常捕获程序(即调用堆栈中某函数使用try.....except语句捕获处理),则执行相应的处理程序(try…except语句中匹配except语句块)。如果堆栈中没有匹配的异常捕获程序,则Python虚拟机捕获处理异常。

Python虚拟机捕获处理异常

如果堆栈中没有匹配的异常捕获程序,则该异常最后传递给Python虚拟机,Python虚拟机通常用异常处理程序在控制台打印出异常的错误信息和调用堆栈,并中止程序的执行

使用try…except…else…finally语句捕获处理异常

Python语言采用结构化异常处理机制,在程序运行过程中,如果程序产生错误,则抛出异常;try语句定义代码块,运行可能抛出的异常的代码;except语句捕获特定异常并执行相应的处理;else语句代码执行无异常时的处理;finally语句保证即是产生异常(处理失败),也可以在事后清理自愿等。try...except...else....finally语句的一般格式为:

try:
	可能产生异常
except Exception1:                #捕获异常Exception1
	发生异常时执行的语句
except (Exception2,Exception3):   #捕获异常Exception2,Exception3
	发生异常时执行的语句
except Exception4 as e:           #捕获异常Exception4,其实例为e
	发生异常时执行的语句
except:                           #捕获其他所有异常
	发生异常时执行的语句
else:                             #无异常
	无异常时执行的语句
finally:                          #不管发生什么与否,保证执行
	不管发生什么,保证执行语句

try语句有以下三种可能的形式:

  1. try...except...[else...]语句:一个try块后接一个或多个except块,可选else
  2. try...finally语句:一个try块后接一个finally
  3. try...except...[else...]finally语句:一个try块后接一个或多个except块,可选else块,后面再跟一个finally

使用try...except...else...finally语句,还可以重新引发异常,即处理部分异常,然后使用else语句重新引发异常,以便调用堆栈中的其他异常处理程序捕获并处理

捕获异常的顺序

except块可以捕获并处理特定的一场类型(此类型称为异常筛选器),具有不同异常筛选器的多个except块可以串联在一起。系统会自动自上至下匹配引发的异常;如果匹配(引发的异常为异常筛选器的类型或子类型),则执行该except块中异常处理代码;否则继续匹配下一个except块。故需要将带有最具体的(即派生程序最高的)异常类的except块放在最前面。

finally块和发生异常后的处理

finally块始终在执行完tryexcept块之后执行,而与是否引发异常或者是找到与异常类型匹配的except块无关。finally块用于清理在try块中执行的操作,如释放其占有的资源(如文件流、数据库连接和图形句柄),而不用等待由运行库中的垃圾回收器来完成对象。

自定义异常类

Python库中提供了许多异常。在应用程序开发过程中,有时候需要自定义特定应用程序的异常类,表示应用程序的一些错误类类型。

自定义异常类一般继承于Exception或子类,处理应用程序中出现负数参数的异常或Exception为后缀

断言处理

断言处理概述

编写程序时,在调试阶段往往需要判断代码执行过程中变量的值等信息(例如:对象是否为空,数值是否为0等)
虽然可以使用print()函数打印输出结果,也可以通过断电跟踪调试查看变量,但使用断言更加灵活高效。断言一般用于下列情况。

  1. 前置条件断言:代码执行之前必须具备的特性
  2. 后置条件断言:代码执行之后必须具备的特性
  3. 前后不变断言:代码执行前后不能变化的特性

断言主要功能是帮助程序员调试程序,以保证程序运行的正确性;断言一般在开发调试阶段使用。即调试模式时使用断言有效,优化模式时,自动忽略断言。
相对的,异常处理则是通过错误的抛出和捕获机制,以保证程序的健壮性;异常处理贯穿与程序开发运行的各个阶段

assert语句和AssertionError类

使用关键字assert可以声明断言,断言声明包括下列两种形式:

assert <布尔表达式>               #简单形式
assert <布尔表达式>,<字符串表达式>  #带参数的形式

其中,<布尔表达式>的结果为一个布尔值(True或False),<字符串表达式>是断言式失败输出的失败消息。在调试模式,如果<布尔表达式>,则抛出AssertionError对象实例。

Pyton解释器有两种运行模式;调试模式优化模式。通常为调试模式,内置只读变量__debug__为True。使用选项-O运行时(即Python.exe -O)为优化模式,此时内置只读变量__debug__为False。故两种形式的assert语句相当于:

if __debug__:
	if not testexpression: raise AssertionError
if __debug__:
	if not testexpression: raise AssertionError(data)

启用/禁用断言

通常Python运行在调试模式,程序中的断言语句可以帮助程序调错。正在运行时,使用运行选项-O,以优化模式运行来禁用断言,从而提高程序的效率

程序的基本调试方法

在程序实际投入运行之前,查找和修正其错误的过程称为调试debugging

语法错误的调试

对于编译错误,Python解释器会直接抛出异常。可以根据输出的异常信息修改程序代码

运行时错误的调试

对于运行时错误,Python解释器会抛出异常,代码中可以通过try...except语句捕获并处理。如果程序中没有try...except,则Python解释器直接打印出异常信息。

逻辑错误的调试

逻辑错误的调试方法,包括断点跟踪、输出信息等方法。有的集成开发环境(IDE)可以设置断点,并查看变量等。

通过print语句输出程序运行过程中的变量值(跟踪信息),是观察和调试程序运行逻辑正确性有效方法。