Python 提供了try except 語句捕獲并處理異常,該異常處理語句的基本語法結(jié)構(gòu)如下:try: 注意,except 后面也可以不指定具體的異常名稱,這樣的話,表示要捕獲所有類型的異常。 另外,從 try except 的基本語法格式可以看出,try 塊僅有一個(gè),但 except 代碼塊可以有多個(gè),這是為了針對(duì)不同的異常類型提供不同的異常處理方式。當(dāng)程序發(fā)生不同的意外情況時(shí),會(huì)對(duì)應(yīng)不同的異常類型,Python 解釋器就會(huì)根據(jù)該異常類型來決定使用哪個(gè) except 塊來處理該異常。通過在 try 塊后提供多個(gè) except 塊可以無須在異常處理塊中使用 if 判斷異常類型,但依然可以針對(duì)不同的異常類型提供相應(yīng)的處理邏輯,從而提供更細(xì)致、更有條理的異常處理邏輯。 try except 語句的執(zhí)行流程如下:
事實(shí)上,不管程序代碼塊是否處于 try 塊中,甚至包括 except 塊中的代碼,只要執(zhí)行該代碼塊時(shí)出現(xiàn)了異常,系統(tǒng)總會(huì)自動(dòng)生成一個(gè) Error 對(duì)象。如果程序沒有為這段代碼定義任何的 except 塊,則 Python 解釋器無法找到處理該異常的 except 塊,程序就會(huì)停止運(yùn)行;反之,如果程序發(fā)生異常,并且該異常經(jīng) try 捕獲并由 except 處理完成,則程序會(huì)繼續(xù)執(zhí)行。 舉個(gè)例子: try:a = int(input("輸入被除數(shù):"))b = int(input("輸入除數(shù):"))c = a / bprint("您輸入的兩個(gè)數(shù)相除的結(jié)果是:", c )except (ValueError, ArithmeticError):print("程序發(fā)生了數(shù)字格式異常、算術(shù)異常之一")except :print("未知異常")print("程序繼續(xù)運(yùn)行") 輸入被除數(shù):a 除此之外,由于 try 塊中引發(fā)了異常,并被 except 塊成功捕獲,因此程序才可以繼續(xù)執(zhí)行,才有了“程序繼續(xù)運(yùn)行”的輸出結(jié)果。 訪問異常信息如果程序需要在 except 塊中訪問異常對(duì)象的相關(guān)信息,可以通過為 except 塊添加as a 來實(shí)現(xiàn)。當(dāng) Python 解釋器決定調(diào)用某個(gè) except 塊來處理該異常對(duì)象時(shí),會(huì)將異常對(duì)象賦值給 except 塊后的異常變量,程序即可通過該變量來獲得異常對(duì)象的相關(guān)信息。所有的異常對(duì)象都包含了如下幾個(gè)常用屬性和方法:
下面例子演示了程序如何訪問異常信息: def foo():try:fis = open("a.txt");except Exception as e:# 訪問異常的錯(cuò)誤編號(hào)和詳細(xì)信息print(e.args)# 訪問異常的錯(cuò)誤編號(hào)print(e.errno)# 訪問異常的詳細(xì)信息print(e.strerror)foo() 在 Python 的早期版本中,直接在單個(gè)異常類或異常類元組(多異常捕獲)之后添加異常變量,中間用逗號(hào)隔開即可。 上面程序調(diào)用了 Exception 對(duì)象的 args 屬性(該屬性相當(dāng)于同時(shí)返回 errno 屬性和 strerror 屬性)訪問異常的錯(cuò)誤編號(hào)和詳細(xì)信息。運(yùn)行上面程序,會(huì)看到如下運(yùn)行結(jié)果:(2, 'No such file or directory') 關(guān)于如何處理異常的傳播軌跡信息,以及使用 open() 方法來打開一個(gè)文件,用于讀取磁盤文件的內(nèi)容,后續(xù)章節(jié)還有更詳細(xì)的介紹,此處暫不詳細(xì)講解。 異常類的繼承體系當(dāng) Python 解釋器接收到異常對(duì)象時(shí),如何為該異常對(duì)象尋找 except 塊呢?注意上面程序中 except 塊的 except Exception,這意味著此 except 塊專門用來處理該異常類以及其子類的異常實(shí)例。當(dāng) Python 解釋器接收到異常對(duì)象后,會(huì)依次判斷該異常對(duì)象是否是 except 塊后的異常類或其子類的實(shí)例,如果是,Python 解釋器將調(diào)用該 except 塊來處理該異常;否則,再次拿該異常對(duì)象和下一個(gè) except 塊里的異常類進(jìn)行比較。 Python 異常捕獲流程示意圖如圖 1 所示: ![]() 圖 1 Python 異常捕獲流程示意圖 從圖 1 中可以看出,在通常情況下,如果 try 塊被執(zhí)行一次,則 try 塊后只有一個(gè) except 塊會(huì)被執(zhí)行,不可能有多個(gè) except 塊被執(zhí)行。除非在循環(huán)中使用了 continue 開始下一次循環(huán),下一次循環(huán)又重新運(yùn)行了 try 塊,這才可能導(dǎo)致多個(gè) except 塊被執(zhí)行。 Python 的所有異常類都從 BaseException 派生而來,提供了豐富的異常類,這些異常類之間有嚴(yán)格的繼承關(guān)系,圖 2 顯示了 Python 的常見異常類之間的繼承關(guān)系。 ![]() 圖 2 Python 的常見異常類之間的繼承關(guān)系 從圖 2 中可以看出,Python 的所有異常類的基類是 BaseException,但如果用戶要實(shí)現(xiàn)自定義異常,則不應(yīng)該繼承這個(gè)基類,而是應(yīng)該繼承 Exception 類。 BaseException 的主要子類就是 Exception,不管是系統(tǒng)的異常類,還是用戶自定義的異常類,都應(yīng)該從 Exception 派生。 下面看幾個(gè)簡單的異常捕獲的例子:try: a = int(input("輸入被除數(shù):")) b = int(input("輸入除數(shù):")) c = a / b print("您輸入的兩個(gè)數(shù)相除的結(jié)果是:", c )except ValueError: print("數(shù)值錯(cuò)誤:程序只能接收整數(shù)參數(shù)")except ArithmeticError: print("算術(shù)錯(cuò)誤")except Exception: print("未知異常")
上面程序中的異常類型,都是非常常見的運(yùn)行時(shí)異常,讀者應(yīng)該記住這些異常,并掌握在哪些情況下可能出現(xiàn)這些異常。 正如在前面程序中所看到的,程序總是把對(duì)應(yīng) Exception 類的 except 塊放在最后,這是為什么呢?想一下圖 1 所示的 Python 異常捕獲流程,可能你就會(huì)明白,如果把 Exception 類對(duì)應(yīng)的 except 塊排在其他 except 塊的前面,Python 解釋器將直接進(jìn)入該 except 塊(因?yàn)樗械漠惓?duì)象都是 Exception 或其子類的實(shí)例),而排在它后面的 except 塊將永遠(yuǎn)也不會(huì)獲得執(zhí)行的機(jī)會(huì)。實(shí)際上,在進(jìn)行異常捕獲時(shí),不僅應(yīng)該把 Exception 類對(duì)應(yīng)的 except 塊放在最后,而且所有父類異常的 except 塊都應(yīng)該排在子類異常的 except 塊的后面( 即:先處理小異常,再處理大異常)。 雖然 Python 語法沒有要求,但在實(shí)際編程時(shí)一定要記住先捕獲小異常,再捕獲大異常。 |
|