Python异常处理
文章目录
一.前言
基本上各个语言都会有针对异常的处理。足以体现出异常处理的重要性。异常处理的重要性往往体现在了后期的维护上。对于开发者而言,如果一段代码有try-catch-finally这样的语句,那么在抛异常的时候,开发者就会很容易定位导致系统出bug的位置,大大节省了代码维护的时间。同时,当一个产品发布出去的时候,如果用户非法输入某些东西,而你的代码里面拥有完善的异常处理机制,那么,开发者就可以通过抛出异常的位置,迅速定位,这对于产品迭代,功能增加,以及增加代码的健壮性都有着很重要的意义(可以大大缩短找bug的时间)
二.错误VS异常
对于新手来说,可能很容易就对这两个概念产生混淆。但实际上,这两个概念是有差别的。
1.错误
错误大致分为三个:
- 语法错误:比如说,你想定义一个类,结果你把class写成了case(即:拼写错误)。不过,这种错误,往往通过你的开发工具就可以很轻易的找出来
- 逻辑错误:代码逻辑有问题,比如说一个60多岁的人,你在程序中把人家定义为“婴儿”,这显然是荒谬的。这种往往就是开发者的主观原因造成的。
- 其他错误。
2.异常
异常一般是指:逻辑和语法都没问题,但是程序在执行过程当中还是出问题了。这种问题往往很隐蔽,很多时候会让人觉得莫名其妙。比如说,你写了这么一个代码:
age = input("请输入年龄")
if age > 60:
print("老年人")
上面这段代码本身没问题,但是如果用户不讲究武德,直接给你输入:qwert。那么这个是一个字符串,根本就不是一个数字,那么就会导致程序出问题。
本篇文章既然要说异常处理,那么主要就是针对异常来说事。
三.常见的系统异常
1.异常分类
异常 | 名称 | 示例 |
---|---|---|
除零异常 | ZeroDivisionError | 2/0 |
名称异常 | NameError | 前面定义了一个name,然后写后续代码的时候协程了nmae |
类型异常 | TypeError | “abc” + 2 |
索引异常 | IndexError | a = [1,2,3,4] b=a[6] |
键值异常 | KeyError | a = {“name”:“Wang”, “age”:20} a[“sex”] |
值异常 | ValueError | int(“a”) |
属性异常 | AttributeError | 一个类里面没有某个属性,但是类对象却去调用 |
迭代器异常 | StopIteration | it = iter([1, 2]) print(next(it)) print(next(it)) print(next(it)) |
2. 系统异常的继承关系
首先,在Python当中,所有的异常都是以BaseException作为父类的,那么通过BaseException往下继承,又可以大致分为四个大类:
-
SystemExit:由sys.exit()函数引发。当它不处理时,Python 解释器退出
-
KeyboardInterrupt:这个是由用户通过键盘手动退出
-
GeneratorExit:当用户关闭generator的时候(generator.close())的时候触发
-
Exception:所有内置的、 非系统退出异常是从该类派生的。如果用户想要自定义一个异常处理,那么都是要继承这里面的各种异常。这个是一个很庞大的类,大体情况如下:
四.异常处理
1. try-except-else语句
这个是最为常用的一个语句,其中except可以有多个,它的语法结构如下:
try:
本来需要执行的逻辑
except 异常类1:
异常类1发生的时候,要执行的东西
except 异常类2:
异常类2发生的时候,需要执行的东西
else:
没有发生异常的时候,需要执行的东西
举个例子:
try:
fp = open("test.txt", "w")
fp.write("test")
except IOError:
print("Error: 没有找到文件或读取文件失败")
else:
print("写入成功")
fp.close()
注意try-except-else的一些要点:
- 如果当try后的语句执行时发生异常,如果这个异常正好是except语句当中有的,那么执行except当中的语句,当该异常处理完毕后,控制流就通过整个try语句。
- 如果在try后的语句里发生了异常,而这个异常却没有在except语句当中做处理,那么该异常就会递交到上层的try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。
注意,一个except语句当中,你可以写多个异常类:
def convert_to_num(var):
try:
return int(var)
except ValueError, Argument:
print("参数没有包含数字\n", Argument)
# 调用函数
convert_to_num("xyz");
2.try-finally
这个语句有一个重要特点:当退出try语句的时候,总会执行finally当中的内容(不管是否出现异常,都会执行finally)
try:
fp = open("test.txt", "w")
try:
fp.write("test")
finally:
print("关闭文件")
fp.close()
except IOError:
print("Error: 没有找到文件或读取文件失败")
如果进入try之后,有异常,那么就会立刻执行finally当中的内容,然后,开始执行except语句
3.raise语句
这个raise语句,是用来触发异常的。raise语句的语法如下:
raise [exceptionName [(reason)]]
其中,用 [] 括起来的为可选参数,exceptionName为触发的异常类型,而reason则是为这个异常添加描述信息。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。
def myLevel( level ):
if level < 1:
raise Exception,"Invalid level!"
# 触发异常后,后面的代码就不会再执行
try:
myLevel(0)
except Exception,err:
print(1,err)
else:
print(2)
输出结果:
Invalid level!
五.自定义异常
作为编程人员,可以定义一个类,同时在构造函数(即:__init()__函数)当中进行类的初始化。用这种方式来自定义一个异常。
class MyException(RuntimeError): # 基于RuntimeError,所以继承RuntimeError
def __init__(self, arg):
self.args = arg
try:
raise MyException("my Exception") # 触发异常
except Networkerror,e:
print e.args