这两天在赶一个线上的程序,用python比较多,整理一点python异常相关的内容。好久没写代码,python功力又下降了,不得不说,啥技能都是要细细打磨的,不用了,忘得就很快。
在python中,我们区分"异常"和"错误"这两个概念。错误,指的是语法不符合编码规范,无法被识别和执行。异常,指的是语法正确,可以被执行,但是在执行过程中遇到了问题,抛出异常。简单举个例子:
[root@VM-0-14-centos ~]# python Python 2.7.5 (default, Aug 7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2 Type "help", "copyright", "credits" or "license" for more information. ---------错误---------- >>> if aaa File "<stdin>", line 1 if aaa ^ SyntaxError: invalid syntax >>> ---------异常-------- >>> 10/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDiVisionError: integer division or modulo by zero
如上所述,if aaa因为后面没有冒号,所以是语法错误,会报一个SyntaxError,表示语法错误;而10/0是因为分母不能为0,所以抛出了异常。
在写程序的过程中,会遇到各种各样的异常,例如:
keyError:代表字典里面找不到值;
FileNotFoundError:读取文件的时候,文件不存在;
ZeroDevisionError:分母不能为0;
NameError:变量没有声明直接使用,例如直接写a*2;
TypeError:变量类型错误,例如1+[1,2];
更详细的异常种类,大家可以看Python的官方文档:
https://docs.python.org/3/library/exceptions.html#bltin-exceptions
这里就不再赘述了。
通常情况下,在Python中我们使用try...execept来处理异常。
如下是使用try execept来捕获一个NameError:
-----测试代码---- [root@VM-0-14-centos hotfix]# cat aa.py # /usr/bin/env python try: a*2 except NameError as err: e=err print e print('hello,world') ------执行结果---- [root@VM-0-14-centos hotfix]# python aa.py name 'a' is not defined hello,world
可以看到,我们的a没有定义,直接乘以2,就发生了NameError,然后我们使用execept将异常保存在err这个变量里面,又赋值给e这个变量,最后打印。
值得注意的是,代码最后面的hello,world也被执行了。
上面的情况下,我们已知异常类型是NameError,然后取捕捉NameError,当然能够捕捉到,如果我们写错了异常类型呢?见下面的代码:
----测试代码--- [root@VM-0-14-centos hotfix]# cat bb.py # /usr/bin/env python try: a*2 except TypeError as err: e=err print e print('hello,world') ----输出结果---- [root@VM-0-14-centos hotfix]# python bb.py Traceback (most recent call last): File "bb.py", line 3, in <module> a*2 NameError: name 'a' is not defined
这次我们修改异常类型为TypeError,然后执行代码,可以看到异常被抛出来了,但是似乎没有上次的输出那么优雅,因为我们没有用TypeError捕获到,所以except后面的内容就没有输出。同时需要注意,最后一行的print语句也没有执行。
通过这两个小例子,可以看到:
1、只有异常类型相符的异常被捕获,才会执行except 代码块的内容,同时也会执行后续内容;
2、如果某个异常没有被捕获,那么后续的代码不会执行;
那么如何解决上面的问题呢?
假如我们知道异常可能是NameError或者TypeError,可以通过下面两种方法来保证能够捕获:
----方法一--- # /usr/bin/env python try: a*2 except NameError as err: print(err) except TypeError as err: print(err) print('hello,world') ----方法二---- # /usr/bin/env python try: a*2 except (NameError, TypeError) as err: print(err) print('hello,world')
如果我们连具体的异常类型都不知道呢?这个时候,就可以使用Python中的异常基类Exception来进行捕获:
----方法三--- # /usr/bin/env python try: a*2 except Exception as err: print(err) ----方法四----(缺省表示Exception) # /usr/bin/env python try: a*2 except: print('err')
需要注意:当一个程序中try后面跟有多个exception的时候,会匹配第一个可以匹配的异常类型。
finally这个语法经常会在开发的时候忘记,下面是一个典型的例子:
import sys try: f = open('aaa.txt', 'r') # some data Processing except OSError as err: print('OS error: {}'.format(err)) except: print('Unexpected error:', sys.exc_info()[0]) finally: f.close()
代码的内容是打开一个文件,然后做一些操作,操作过程中如果遇到了异常,则捕获这些异常。
上面的写法中,无论程序中的try语法模块中的操作执行成功还是失败,都会执行最后面的finally,finally语法最后面经常会写一些无论如何都要执行的语句。
异常处理的使用需要根据场景来确定,不能不用,不用的话代码稳定性不高;也不能滥用,滥用的话,会显得代码很冗余。
例如下面的2个小场景:
----场景一---- try: data = json.loads(raw_data) except JSONDecodeError as err: print('JSONDecodeError: {}'.format(err)) 解析json前,对json进行合法性判断是有必要的, 否则json的解析就会报错。 ---场景二---- d = {'name': 'jason', 'age': 20} try: value = d['dob'] except KeyError as err: print('KeyError: {}'.format(err)) 字典的key值解析这种写法也可以,但是显得有点繁琐 最好的写法是: if 'dob' in d: xxxxx