1. 异常处理
异常就是在程序执行过程中发生的超出预期的事件。一般情况下,当程序无法正常执行时,都会抛出异常。
- 在开发过程中,由于疏忽或考虑不周,出现的设计错误。因此,在后期程序调试中应该根据错误信息,找到出错位置,并对错误代码进行分析和排错。
- 难以避免的运行问题,如读写权限不一致、网络异常、文件不存在、内存溢出、数据类型不一致等。针对这类异常,在设计时可以主动捕获异常,防止程序意外终止。
- 主动抛出异常。在程序设计中,可以根据设计需要主动抛出异常,引导程序按照预先设计的逻辑运行,防止用户不恰当地操作。
当发生异常时,一般应该捕获异常,并妥善处理。如果异常未被处理,程序将终止运行,因此为程序添加异常处理,能使程序更健壮。
异常也是一种类型,所有的异常实例都继承自BaseException
基类。使用Exception
可以捕获所有类型的异常。
使用try…except
语句可以捕获和处理异常,语句格式如下:
try: # 捕获异常
<执行语句>
except [异常类型[ as 别名]]: # 处理异常
<处理语句>
把目标代码放在try
语句中,在except
关键字后面设置可选的异常类型。如果省略异常类型,则表示捕获全部异常类型。如果类型的名称比较长,可以使用as关键字设置别名,方便在异常处理中引用。如果不需要处理异常,可以在except
代码块中使用pass
语句忽略。
try:
'1' + 0 # 错误运算
except: # 捕获所有的异常
print('发生错误') # 提示错误信息
try:
f = open('test.txt','r') # 打开不存在的文件
except IOError as e: # 捕获IOError类型异常
print('错误编号:%s,错误信息:%s'%(e.errno,e.strerror)) #显示错误信息
try:
f = open('test.txt','r') # 打开不存在的文件
except Exception as e: # 捕获IOError类型异常
print('错误编号:%s,错误信息:%s'%(e.errno,e.strerror)) #显示错误信息
使用except语句可以同时处理多个异常,语法格式如下:
# 多个异常类型以元组形式进行设置,当try代码发生其中一个,都被except处理。
try: # 捕获异常
<执行语句>
except ([Exception1[,Exception2,...,ExceptionN]]) [ as 别名]: # 处理异常
<处理语句>
# 多个异常类型按优先顺序分别进行处理
try: # 捕获异常
<执行语句>
except ([Exception1[,Exception2]]) [ as 别名]:
<处理语句1>
...
except [Exception]:
<处理语句N>
# 样例
li = [] # 定义空列表
try:
print(c) # 发生NameError异常
print(3/0) # 发生ZeroDivisionError异常
li[2] # 发生IndexError异常
a = 123 + 'hello world' # 发生TypeError异常
except NameError as e:
print('出现NameError异常!',e)
except ZeroDivisionError as e:
print('出现ZeroDivisionError异常!',e)
except IndexError as e:
print('出现IndexError异常!',e)
except TypeError as e:
print('出现TypeError异常!',e)
except Exception as e:
print('出现其它异常!',e)
在try
代码块中可以嵌套使用try…except
结构,设计多层嵌套的异常处理结构,异常对象可以从内向外逐层向上传递。
try:
try:
try:
f = open('test.txt','r') # 打开并不存在的文件
except NameError as e: # 捕获未声明的变量异常
print('NameError')
except IndexError as e: # 捕获索引超出异常
print('IndexError')
except IOError as e: # 获取输入或输出的异常
print('IOError')
# 输出:IOError
try…except
异常处理结构允许附带一个可选的else
子句,用来设计当没有发生异常时,正常处理的代码块。else
子句在异常发生时,不会被执行,只有当异常没有发生时才会执行。
try: # 捕获异常
<执行语句>
except [异常类型][ as 别名]: # 处理异常
<异常处理语句>
else: # 当异常未发生时执行
<正常处理语句>
try:
f = open('test.txt','r') # 打开文件
except: # 如文件打开异常,则执行此
print('出错了')
else: # 如文件正常打开,则执行此
print(f.read()) # 读取文件内容
try:
f = open('test.txt','r') # 打开文件
print(f.read()) # 如文件打开异常,则不执行此
except: # 如文件打开异常,则执行此
print('出错了')
善后处理:try…except
异常处理结构还可以带一个可选的finally
子句,它表示无论异常是否发生,最后都要执行finally
语句块。
try: # 捕获异常
<执行语句>
except [异常类型][ as 别名]: # 处理异常
<异常处理语句>
else: # 当异常未发生时执行
<正常处理语句>
finally: # 不管异常是否发生,最后都要执行
<最后必须处理语句>
import time
i = 1
while True: # 无限循环
try:
print(i) # 打印变量
continue # 退出执行下一次循环
print('永不执行') # 不执行该句
finally:
time.sleep(1) # 暂停1s
i += 1 # 递增变量
continue # 退出执行下一次循环
print('永不执行') # 不执行语句
抛出异常:使用raise
语句主动抛出一个异常,这样可以确保程序根据开发人员的设计逻辑执行,也可以对用户的行为进行监控,避免意外操作。
raise [Exception[(args[,traceback])]]
raise 语句3种用法:
raise
:单独一个raise。该语句将引发当前上下文捕获的异常,默认引发RuntimeError
异常。raise 异常类名称
:raise 后带一个异常类名称,表示引发执行指定类型的异常。raise 异常类名称(描述信息)
:在引发指定类型的异常时,显示异常的描述信息。
try:
a = input('输入一个数')
if (not a.isdigit()):
raise
except RuntimeError as e:
print('引发异常:',repr(e)) # 引发异常: RuntimeError('No active exception to reraise')
def test(num):
try:
if type(num) != int: # 如果为非数字的值,则抛出TypeError错误
raise TypeError('参数不是数字')
if num <= 0: # 如果为非正整数,则抛出ValueError错误
raise ValueError('参数为不大于0的整数')
print(num) # 打印数字
except Exception as e:
print(e) # 打印错误信息
test('1') # 参数不是数字
test(0) # 参数为不大于0的整数
test(2) # 2
自定义异常:自定义异常类型必须直接或间接继承Exception
类。
class MyError(Exception):
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
try:
raise MyError('自定义错误信息') # 主动抛出自定义错误
except MyError as e:
print(e) # 打印:自定义错误信息
跟踪异常:使用traceback
可以跟踪异常,记录异常发生时有关函数调用的堆栈信息。具体格式如下:
import traceback
try:
1/0
except Exception as e:
traceback.print_exc() # 打印详细的错误信息
traceback.format_exc() # 格式化字符串的形式返回错误信息
# 把错误信息直接保存到外部文件中
traceback.print_exc(file=open('log.log',mode='a',encoding='utf-8'))
2. 程序调试
在编写程序中,常见错误有两种:语法错误和异常。语法错误又称解析错误,SyntaxError: invalid syntax 。
即使语句或表达式在语法上是正确的,但在执行时也可能会引发错误,该错误称为异常,异常如果不被捕获,被程序处理,则会中止程序,并显示异常提示信息。
使用assert
语句可以定义断言,断言用于判断一个表达式,在表达式条件为Flase的时候触发异常,而不必等待程序运行后出现崩溃的情况。
def foo(s):
n = int(s)
assert n != 0,'n is zero!' # 设置断言
return 10/n
def main():
foo('0')
main()
# 在交互式模式中,可以使用-O参数关闭 assert
python -O test1.py
使用pdb,pdb 是Python自带的一个包,提供了一种交互的源代码调试功能,主要特性包括:设置断点、单步调试、进入函数调试、查看当前代码、查看栈片段、动态改变变量的值等。pdb常用的调试命令:
- break或b:设置断点。
- continue或c:继续执行程序。
- list或l:查看当前行的代码段。
- step或s:进入函数。
- return或r:执行代码直到从当前函数返回。
- exit或q:中止并退出。
- next或n:执行下一行。
- p val:打印变量的值。
- help:帮助。
# 新建test1.py文件,代码如下
s = '0'
n = int(s)
print(10/n)
# cd 命令进入到test1.py文件所在目录
cd edition_master
# 启动pdb调试器
python -m pdb test1.py
# 执行操作
l # 查看当前行的代码段
n # 下一步
p s # 打印变量s
q # 退出
当程序代码较多时,在可能出错的地方插入pdb.set_trace()
,则不需要单步执行。