Python提供了多种不同形式的异常处理结构,基本思路都是一致的:先尝试运行代码,然后处理可能发生的错误。在实际使用时可以根据需要选择使用哪一种。
1 try ... except ...
Python异常处理结构中最基本的额结构是 try ... except ... 结构。其中 try 子句中的代码块包含可能会引发异常的语句,而except子句用来捕捉相应的异常。如果try子句中的代码块引发异常并被except子句捕获,则执行except子句的代码块;如果try中的代码扩没有出现异常则继续往下执行浴场处理结构后面的代码;如果出现异常但没有被except捕获,则继续往外抛出;如果所有层都没有捕获并处理该异常,则程序崩溃并将该异常呈现给最庸用户,这是我们最不希望发生的事情。该结构语法如下:
try:#可能会引发异常的代码
except Exception[ as resaon]:#如果try中的代码抛出异常并被except捕捉,就执行这里的代码
1 whileTrue:2 x = input('Please input:')3 try:4 x =int(x)5 print('you have input {0}'.format(x))6 break
7 exceptException as e:8 print('Error.')9
10 '''
11 Please input:t12 Error.13 Please input:u14 Error.15 Please input:716 you have input 717 '''
小提示:一般而言,应避免捕获Python异常类的基类 BaseException,而应该明确指定捕获哪种异常,然后有针对性地编写的处理代码。
拓展知识:回调函数原理。6.4.1节介绍shutil模块时曾经提到过回调函数的概念,不知道大家有没有想过什么是回调函数,回调函数又是怎么实现的呢?回调函数的定义与普通函数没有本质的区别,但一般不直接调用,而是作为参数传递给另一个函数,当另一个函数中触发了某个事件、满足了某个条件时就会自动调用回调函数。用下面代码实现删除包含只读属性文件的文件夹,下面的代码实现同样的功能,主要用来演示回调函数的原理。
1 importos2 importstat3
4 def remove_readonly(func,path): #定义回调函数
5 os.chmod(path,stat.S_IWRITE) #删除文件的只读属性
6 func(path) #再次调用刚刚失败的函数
7
8 def del_dir(path,οnerrοr=None):9
10 for file in os.listdir(path): #获取目录下的文件和文件夹
11
12 file_or_dir = os.path.join(path,file) #拼接路径和文件或文件夹名,形成以绝对路径的文件名或文件夹名
13
14 if os.path.isdir(file_or_dir) and not os.path.islink(file_or_dir): #判断是文件且不是链接的就删除
15 del_dir(file_or_dir)16 else:17 try:18 os.remove(file_or_dir) #尝试删除该文件
19 except:20 if onerror and callable(onerror): #如果第二个参数为True 且可调用
21 onerror(os.remove,file_or_dir)22 else:23 print('You have an exception but did not capture it.')24 os.rmdir(path) #调用函数,删除文件夹
25
26 del_dir(r'E:\old',remove_readonly) #调用函数,指定回调函数
2 try ... except ... else ...
带有else子句的异常处理结构可以看作是一种特殊的选择结构,如果try中的代码抛出了异常并且被某个except语句捕获则执行相应的异常处理代码,这种情况下就不会执行else中的代码;如果try中的代码没有抛出异常,则执行else块的代码。该结构的语法如下:
1 try:2 #可能会引发异常的代码
3 exceptException [as reason]:4 #用来处理异常的代码
5 else:6 #如果try子句中的代码没有引发异常,就继续执行这里的代码
例如,前面要求用户必须输入整数的代码也可以这样写:
1 whileTrue:2 x = input('please input')3 try:4 int(x)5 exceptException as e:6 print('Error.')7 else:8 print('You have input {0}'.format(x))9 break
10
11 '''
12 please inputa13 Error.14 please input v15 Error.16 please input317 You have input 318 '''
3 try ... except ... finally ...
在这种结构中,无论try中的代码是否发生异常,也不管抛出的异常有没有被except语句捕获,finally子句中的代码总是会得到执行。因此,finally中的代码常用来做一些清理工作以释放try子句中申请的资源。该结构语法为
1 try:2 #可能会引发异常的代码
3 exceptException [as reason]:4 #用来处理异常的代码
5 finally:6 #无论try子句中的代码是否引发异常,都会执行这里的代码
例如下面的代码,不论是否发生异常,finally子句中的代码总是被执行。
1 defdiv(a,b):2 try:3 print(a/b)4 exceptZeroDivisionError:5 print('The second parameter connot be 0.')6 finally:7 print(-1)8
9 div(3,5)10 '''
11 0.612 -113 '''
14
15 div(3,0)16 '''
17 The second parameter connot be 0.18 -119 '''
注意:如果try子句中的异常没有被except语句捕获和处理,或者except子句或else子句中的代码抛出了异常,那么这些异常将会在finally子句执行完后再次抛出。
1 defdiv(a,b):2 try:3 print(a /b)4 exceptZeroDivisionError:5 print('The second parameter connot be 0.')6 finally:7 print(-1)8
9 div('3',5)10
11 '''
12 -113 Traceback (most recent call last):14 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 9, in 15 div('3',5)16 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 3, in div17 print(a / b)18 TypeError: unsupported operand type(s) for /: 'str' and 'int'19 '''
注意:finallly子句中的代码也可能引发异常。下面的代码的本意是使用异常处理结构来避免文件对象没有关闭的情况发生,但是由于指定的文件不存在而导致打开失败,结果在finally子句中关闭文件时引发了异常。
1 try:2 f1 = open('test1.txt','r',encoding='utf-8') #文件不存在抛出异常,不会创建文件对象f1
3 line = f1.readline() #后面的代码不会被执行
4 print(line)5 exceptSyntaxError:6 print('Sth wrong')7 finally:8 f1.close() #f1不存在,再次引发异常
9
10 '''
11 Traceback (most recent call last):12 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 2, in 13 f1 = open('test1.txt','r',encoding='utf-8') #文件不存在抛出异常,不会创建文件对象f114 FileNotFoundError: [Errno 2] No such file or directory: 'test1.txt'15
16 During handling of the above exception, another exception occurred:17
18 Traceback (most recent call last):19 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 8, in 20 f1.close() #f1不存在,再次引发异常21 NameError: name 'f1' is not defined22 '''
注意:如果在函数中使用异常处理结构,尽量不要在finally子句中使用return语句,以免发生非常难以发现的逻辑错误。例如下面的代码,不管参数是否符合函数要求,调用函数时都得到了同样的错误信息。
1 defdiv(a,b):2 try:3 return a /b4 exceptZeroDivisionError:5 return 'The second parameter connot be 0.'
6 finally:7 return 'Error.'
8
9 print(div(3,5))10 print(div('3',5))11 print(div(3,0))12
13 '''
14 Error.15 Error.16 Error.17 '''
4 可以捕捉更多异常的异常处理结构
在实际开发中,同一段代码可能会抛出多种异常,并且需要针对不同的异常类型进行相应的处理。为了支持多种异常的捕捉和处理,Python提供了带有多个except的异常处理结构,一旦某个except捕捉到了异常,则其他的except子句将不会再尝试捕捉异常。该结构类似于多分支选择结构,语法格式为:
1 try:2 #可能会引发异常的代码
3 exceptException1:4 #处理异常类型 1 的代码
5
6 exceptException1:7 #处理异常类型 1 的代码
8
9 exceptException1:10 #处理异常类型 1 的代码
11
12 .13 .14 .15
下面的代码演示了这种异常处理结构的用法,连续运行3次并输入不同的数据,结果如下:
1 try:2 x = float(input('请输入被除数:'))3 y = float(input('请输入除数:'))4 z = x /y5 exceptZeroDivisionError:6 print('除数不能为零')7 exceptTypeError:8 print('被除数和整数应为数值类型')9 exceptNameError:10 print('变量不存在')11 else:12 print(x,'/',y,'=',z)13
14 '''
15 请输入被除数: 3016 请输入除数: 517 30.0 / 5.0 = 6.018
19
20 请输入被除数: 3021 请输入除数: abc22 Traceback (most recent call last):23 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 3, in 24 y = float(input('请输入除数: '))25 ValueError: could not convert string to float: 'abc'26
27 请输入被除数: 3028 请输入除数: 029 除数不能为零30 '''
5 同时包含else子句、finally子句和多个except子句的异常处理结构
Python异常处理结构中可以同时包含多个except子句、else子句和finally子句,例如:
1 defdiv(x,y):2 try:3 print( x /y)4 exceptZeroDivisionError:5 print('ZeroDivisionError')6 exceptTypeError:7 print('TypeError')8 else:9 print('No Error')10 finally:11 print('exceuting finally clause')12
13
14 div(3,5)15 '''
16 0.617 No Error18 exceuting finally clause19 '''
20
21 div(3,0)22 '''
23 ZeroDivisionError24 exceuting finally clause25 '''
拓展知识:断言语句 assert 也是一种比较常用的技术,常用阿里在程序的某个位置确认指定条件必须满足,常和异常处理结构一起使用。断言语句assert仅当脚本的__debug__属性值为True时有效,一般只在开发和测试阶段使用。当使用-O选项把Python程序编译成字节码文件时,assert语句将被删除。
1 a = 3
2 b = 5
3
4 #assert a == b,'a must be equal to b'
5
6 '''
7 Traceback (most recent call last):8 File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 4, in 9 assert a == 5,'a must be equal to b'10 AssertionError: a must be equal to b11 '''
12
13 try:14 assert a == b,'a must be equal to b'
15 exceptAssertionError as reason:16 print('%s:%s'%(reason.__class__.__name__,reason))17
18 #AssertionError:a must be equal to b