7.1.3 常見異常處理結構形式


  Python提供了多種不同形式的異常處理結構,基本思路都是一致的:先嘗試運行代碼,然后處理可能發生的錯誤。在實際使用時可以根據需要選擇使用哪一種。

  1 try ... except ... 

  Python異常處理結構中最基本的額結構是 try ... except ... 結構。其中 try 子句中的代碼塊包含可能會引發異常的語句,而except子句用來捕捉相應的異常。如果try子句中的代碼塊引發異常並被except子句捕獲,則執行except子句的代碼塊;如果try中的代碼擴沒有出現異常則繼續往下執行浴場處理結構后面的代碼;如果出現異常但沒有被except捕獲,則繼續往外拋出;如果所有層都沒有捕獲並處理該異常,則程序崩潰並將該異常呈現給最庸用戶,這是我們最不希望發生的事情。該結構語法如下:

  

try:   #可能會引發異常的代碼
 except Exception[ as resaon]:   #如果try中的代碼拋出異常並被except捕捉,就執行這里的代碼

 

 

 1 while True:  2     x = input('Please input:')  3     try :  4         x = int(x)  5         print('you have input {0}'.format(x))  6         break
 7     except Exception as e:  8         print('Error.')  9 
10 '''
11 Please input:t 12 Error. 13 Please input:u 14 Error. 15 Please input:7 16 you have input 7 17 '''

 

  小提示:一般而言,應避免捕獲Python異常類的基類 BaseException,而應該明確指定捕獲哪種異常,然后有針對性地編寫的處理代碼。

  拓展知識:回調函數原理。6.4.1節介紹shutil模塊時曾經提到過回調函數的概念,不知道大家有沒有想過什么是回調函數,回調函數又是怎么實現的呢?回調函數的定義與普通函數沒有本質的區別,但一般不直接調用,而是作為參數傳遞給另一個函數,當另一個函數中觸發了某個事件、滿足了某個條件時就會自動調用回調函數。用下面代碼實現刪除包含只讀屬性文件的文件夾,下面的代碼實現同樣的功能,主要用來演示回調函數的原理。

 1 import os  2 import stat  3 
 4 def remove_readonly(func,path):     #定義回調函數
 5     os.chmod(path,stat.S_IWRITE)      #刪除文件的只讀屬性
 6     func(path)                        #再次調用剛剛失敗的函數
 7 
 8 def del_dir(path,onerror=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 except Exception [as reason]: 4     #用來處理異常的代碼
5 else: 6     #如果try子句中的代碼沒有引發異常,就繼續執行這里的代碼

例如,前面要求用戶必須輸入整數的代碼也可以這樣寫:

 1 while True:  2     x = input('please input')  3     try:  4  int(x)  5     except Exception as e:  6         print('Error.')  7     else:  8         print('You have input {0}'.format(x))  9         break
10  
11  '''
12 please inputa 13 Error. 14 please input v 15 Error. 16 please input3 17 You have input 3 18  '''  

 

  3 try ... except ... finally ...

在這種結構中,無論try中的代碼是否發生異常,也不管拋出的異常有沒有被except語句捕獲,finally子句中的代碼總是會得到執行。因此,finally中的代碼常用來做一些清理工作以釋放try子句中申請的資源。該結構語法為

 

1 try: 2     #可能會引發異常的代碼
3 except Exception [as reason]: 4     #用來處理異常的代碼
5 finally: 6     #無論try子句中的代碼是否引發異常,都會執行這里的代碼

 

例如下面的代碼,不論是否發生異常,finally子句中的代碼總是被執行。

 1 def div(a,b):  2     try:  3         print(a/b)  4     except ZeroDivisionError:  5         print('The second parameter connot be 0.')  6     finally:  7         print(-1)  8 
 9 div(3,5) 10 '''
11 0.6 12 -1 13 '''
14 
15 div(3,0) 16 '''
17 The second parameter connot be 0. 18 -1 19 '''

注意:如果try子句中的異常沒有被except語句捕獲和處理,或者except子句或else子句中的代碼拋出了異常,那么這些異常將會在finally子句執行完后再次拋出。

 1 def div(a,b):  2     try:  3         print(a / b)  4     except ZeroDivisionError:  5         print('The second parameter connot be 0.')  6     finally:  7         print(-1)  8 
 9 div('3',5) 10 
11 '''
12 -1 13 Traceback (most recent call last): 14  File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 9, in <module> 15  div('3',5) 16  File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 3, in div 17  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 except SyntaxError:  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 <module> 13  f1 = open('test1.txt','r',encoding='utf-8') #文件不存在拋出異常,不會創建文件對象f1 14 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 <module> 20  f1.close() #f1不存在,再次引發異常 21 NameError: name 'f1' is not defined 22 '''

注意:如果在函數中使用異常處理結構,盡量不要在finally子句中使用return語句,以免發生非常難以發現的邏輯錯誤。例如下面的代碼,不管參數是否符合函數要求,調用函數時都得到了同樣的錯誤信息。

 1 def div(a,b):  2     try:  3         return a / b  4     except ZeroDivisionError:  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 except Exception1:  4     #處理異常類型 1 的代碼
 5 
 6 except Exception1:  7     #處理異常類型 1 的代碼
 8 
 9 except Exception1: 10     #處理異常類型 1 的代碼
11 
12 . 13 . 14 . 15     

 

下面的代碼演示了這種異常處理結構的用法,連續運行3次並輸入不同的數據,結果如下:

 1 try:  2     x = float(input('請輸入被除數: '))  3     y = float(input('請輸入除數: '))  4     z = x / y  5 except ZeroDivisionError:  6     print('除數不能為零')  7 except TypeError:  8     print('被除數和整數應為數值類型')  9 except NameError: 10     print('變量不存在') 11 else: 12     print(x,'/',y,'=',z) 13 
14 '''
15 請輸入被除數: 30 16 請輸入除數: 5 17 30.0 / 5.0 = 6.0 18 
19 
20 請輸入被除數: 30 21 請輸入除數: abc 22 Traceback (most recent call last): 23  File "C:/Users/dddd/AppData/Local/Programs/Python/Python35/test1.py", line 3, in <module> 24  y = float(input('請輸入除數: ')) 25 ValueError: could not convert string to float: 'abc' 26 
27 請輸入被除數: 30 28 請輸入除數: 0 29 除數不能為零 30 '''

 

 

  5 同時包含else子句、finally子句和多個except子句的異常處理結構

Python異常處理結構中可以同時包含多個except子句、else子句和finally子句,例如:

 1 def div(x,y):  2     try:  3         print( x / y)  4     except ZeroDivisionError:  5         print('ZeroDivisionError')  6     except TypeError:  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.6 17 No Error 18 exceuting finally clause 19 '''
20 
21 div(3,0) 22 '''
23 ZeroDivisionError 24 exceuting finally clause 25 '''

 

拓展知識:斷言語句 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 <module>  9  assert a == 5,'a must be equal to b' 10 AssertionError: a must be equal to b 11 '''
12 
13 try: 14     assert a == b,'a must be equal to b'
15 except AssertionError as reason: 16     print('%s:%s'%(reason.__class__.__name__,reason)) 17 
18 # AssertionError:a must be equal to b


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM