有时程序错误称为程序异常,相信每一位写程序的人一定会碰上程序错误,过去碰上这类情况将终止执行,同时出现错误信息,错误信息内容通常显示Traceback,然后列出异常报告。Python提供的功能可以让我们捕捉异常和撰写异常处理程序,当发生异常时被我们捕捉到则会去执行异常处理程序,然后程序可以继续执行。
设计一个程序异常
以一个除数为0的错误开始说明。
实例1
例子:def division(x, y):
return x / y
print(division(10, 2))
print(division(5, 0))
print(division(6, 3))
执行结果:
Traceback (most recent call last):
File "C:/Users/mac/PycharmProjects/pythonProject/main.py", line 4, in
print(division(5, 0))
File "C:/Users/mac/PycharmProjects/pythonProject/main.py", line 2, in division
return x / y
ZeroDivisionError: division by zero
5.0
上述程序在执行第3行时,一切还是正常。但是执行第4行,因为第2个参数是0,导致ZeroDivisionError: division by zero的错误,所以整个程序执行终止。其实对于上述程序而言,若是程序可以执行第5行,是可以正常得到执行结果的,可是程序第4行已经造成程序终止了,所以无法执行第5行。
发生异常被捕捉时程序会执行异常处理程序,然后跳开异常位置,再继续往下执行。这时要使用try - except指令,它的语法格式如下:
try:
指令 # 预先设想可能引发错误异常的指令
except 异常对象1: # 若以以上执行结果来说,异常对象就是指ZeroDivisionError
异常处理程序1 # 通常是指出异常原因,方便修正
上述会执行try:下面的指令,如果正常则跳离except部分,如果指令异常,则检查此异常是否是异常对象所指的错误;如果是,代表异常被捕捉了,则执行此异常对象下面的异常处理程序。
重新设计实例1,增加异常处理程序
实例2
def division(x, y):
try:
return x / y
except ZeroDivisionError:
print("除数不可为0")
print(division(10, 2))
print(division(5, 0))
print(division(6, 3))
执行结果:
5.0
除数不可为0
None
2.0
上述程序执行第6行时,会将参数(10,2)带入division()函数,由于执行try的指令的 “x/y” 没有问题,所以可以执行 “return x / y”,这是Python将跳过except的指令。当程序执行第7行时,会将参数(5,0)带入division()函数,由于执行try的指令的 “x / y” 产生了除数为0的ZeroDivisionError异常,这时Python会寻找是否有处理这类异常的except ZeroDivisionError存在,如果有就表示此异常被捕捉,就去执行相关的错误处理程序,此例是执行第5行,输出“除数不可为0”的错误。函数返回然后输出结果None,None是一个对象,表示结果不存在,最后返回程序第8行,继续执行相关指令。
从上述案例可以看到,程序增加了try - except后,若是异常问题被except捕捉,则出现的异常信息比较好处理,同时不会有程序中断的情况发生。
特别需要留意的是在try - except的使用中,如果在try:后面的指令产生异常,则这个异常不是我们设计的except异常对象,表示异常没被捕捉到,这时程序依旧会像实例1一样,直接出现错误信息,然后程序终止。
重新设计实例2,但在程序第9行使用字符呼叫除法运算,造成程序异常。
实例3
def division(x, y):
try:
return x / y
except ZeroDivisionError:
print("除数不可为0")
print(division(10, 2))
print(division('a', 'b'))
print(division(6, 3))
执行结果:
5.0
Traceback (most recent call last):
File "C:/Users/mac/PycharmProjects/pythonProject/main.py", line 7, in
print(division('a', 'b'))
File "C:/Users/mac/PycharmProjects/pythonProject/main.py", line 3, in division
return x / y
TypeError: unsupported operand type(s) for /: 'str' and 'str'
由上述执行结果可以看到异常原因是TypeError,由于我们在程序中没有设计except TypeError的异常处理程序,所以程序会终止执行。要解决这类问题需要多设计一组try - except。
扩充设计实例3,增加TypeError异常的捕捉
实例4
def division(x, y):
try:
return x / y
except ZeroDivisionError:
print("除数不可为0")
except TypeError:
print("除法数据型态不符")
print(division(10, 2))
print(division('a', 'b'))
print(division(6, 3))
执行结果:
5.0
除法数据型态不符
None
2.0
Python在try - except中又增加了else指令,这个指令存放的主要目的是try内的指令正确时,可以执行else内的指令区块,我们可以将这部分指令区块称为正确处理程序,这样可以增加程序的可读性。语法格式如下:
try:
指令
except 异常对象1:
异常处理程序1
else:
正确处理程序
使用try - except - else重新设计实例3
实例5
def division(x, y):
try:
ans = x / y
except ZeroDivisionError:
print("除数不可为0")
else:
return ans
print(division(10, 2))
print(division(5, 0))
print(division(6, 3))
执行结果:与实例2相同
程序设计时经常发生的异常是打开文档时找不到文档,这是会有FileNotFoundError异常。
实例6
fn = 'data.txt'
try:
with open(fn) as file_Obj:
data = file_Obj.read()
except FileNotFoundError:
print("找不到 %s 文档" % fn)
else:
print(data)
执行结果:
找不到 data.txt 文档
AttributeError | 通常是指对象没有这个属性 |
Exception | 一般错误皆可用 |
FileNotFoundError | 找不到open()打开的文档 |
IOError | 在输入或输出时发生错误 |
IndexError | 索引超出范围区间 |
KeyError | 在映射中没有这个键 |
MemoryError | 需求内存空间超出范围 |
NameError | 对象名称未声明 |
SyntaxError | 语法错误 |
SystemError | 直译器的系统错误 |
TypeError | 数据类型错误 |
ValueError | 输入无效参数 |
ZeroDivisionError | 除数为零 |
Python的关键词finally的功能是和try配合使用,在try之后可以有except或else,这个finally关键词必须放在except和else之后,不论是否有异常发生,一定会执行这个finally内的程序代码。
try:
指令
except 异常对象:
异常处理程序
finally:
一定会执行
这个功能主要是用在Python程序与数据库连接时,输出连接相关信息。
try - except - finally的应用,读者可以发现无论是否有异常,都会执行finally
实例7
def division(x, y):
try:
return x / y
except:
print("异常发生")
finally:
print("阶段任务完成")
print(division(10, 2),"\n")
print(division(5, 0),"\n")
print(division('a', 'b'),"\n")
print(division(6, 3),"\n")
执行结果:
阶段任务完成
5.0异常发生
阶段任务完成
None异常发生
阶段任务完成
None阶段任务完成
2.0
上述程序执行时,如果没有发生异常,则程序会先输出字符串“阶段任务完成”然后返回主程序,输出division()的返回值。如果程序有异常则会先输出字符串“异常发生”,再执行finally的程序代码输出字符串“阶段任务完成”,然后返回主程序输出“None”。