第11課 異常處理

一、異常處理的意義

1. 異常機(jī)制已經(jīng)成為衡量一門編程語言是否成熟的標(biāo)準(zhǔn)之一,使用異常處理機(jī)制的Python程序會(huì)有更好的容錯(cuò)性。

2. 沒有人能夠保證自己寫的程序永遠(yuǎn)不會(huì)出錯(cuò),既是程序沒有錯(cuò)誤,也不能保證用戶按你的意圖來輸入,另外還有系統(tǒng)的穩(wěn)定性,計(jì)算硬件是否損壞,網(wǎng)絡(luò)掉線等諸多情況。

二、異常處理機(jī)制

1. 使用try...except捕獲異常

1)例如,通常在程序運(yùn)行時(shí),用戶可以隨意輸入,程序不會(huì)因?yàn)橛脩舻妮斎氩缓戏ǘ蝗婚g退出,而是向用戶提示輸入不合法,并請(qǐng)用戶再次輸入。

那么我們就希望有一種強(qiáng)大的if塊來解決這個(gè)非法輸入的問題:

if 用戶輸入不合法:

? ? alert 輸入不合法

? ? go retry

else:

????#業(yè)務(wù)實(shí)現(xiàn)代碼

2)但是“用戶輸入不合法”這個(gè)條件怎么定義呢?我們可以使用正則表達(dá)式與用戶的輸入進(jìn)行匹配。但現(xiàn)實(shí)中不合法的情況非常多,想讓程序一次處理所有的錯(cuò)誤,我們可以將上面的偽代碼修改為:

if 一切正常:

? ? #業(yè)務(wù)實(shí)現(xiàn)代碼

就是:

? ? alert 輸入不合法

? ? goto retry

3) 但“一切正?!币廊皇呛艹橄蟮?,無法轉(zhuǎn)化成代碼。在這種情形下Python提供了一種假設(shè):如果程序可以順利運(yùn)行,那么就是“一切正?!薄?b>由此得出Python異常處現(xiàn)機(jī)制的語法結(jié)構(gòu):

try:

? ? #業(yè)務(wù)實(shí)現(xiàn)代碼

except(Error1, Error2, ....) as e:

? ? alert 輸入不合法

? ? goto retry

4) 這樣如果執(zhí)行try塊里的業(yè)務(wù)實(shí)現(xiàn)代碼是出現(xiàn)異常,系統(tǒng)會(huì)自動(dòng)生成一個(gè)異常對(duì)象,該異常對(duì)象被提交給Python解釋器,這個(gè)過程被稱為引發(fā)異常。

5)當(dāng)Python解釋器收到異常對(duì)象時(shí),會(huì)尋找處理該異常對(duì)象的except塊,如果找到合適的except塊,則把該過程稱為捕捉異常。如果Python解釋器找到不捕獲異常的except塊,則運(yùn)行時(shí)環(huán)境終止,Python解釋器也將退出。

6)不管代碼塊是否處于try中,或except塊中,只要執(zhí)行該代碼時(shí)了現(xiàn)了異常,系統(tǒng)總會(huì)自動(dòng)生成一個(gè)Error對(duì)象。

7) try 可以有多個(gè)except塊,這是為了針對(duì)不同的異常類提供不同的異常處理方式。當(dāng)系統(tǒng)發(fā)生不同意外情況時(shí),系統(tǒng)會(huì)生成不同的異常對(duì)象。如果try被執(zhí)行一次,,則try后面只有一個(gè)except被執(zhí)行,除非放在循環(huán)中,使用continue開始下一次循環(huán)。

2. 異常類

1)Python的所有異常類的基類是BaseException, 但是如果用戶果自定義異常,則一概繼承Exception類。

2)BaseException的主要子類是Exception,所在不管是系統(tǒng)的異常類,還是用戶自定義異常類,都是從Exception派生。

3)異常捕獲實(shí)例:

>>> import sys

>>> try:

a = int(sys.argv[1]) # 代表當(dāng)前運(yùn)行的程序所提供的第一個(gè)參數(shù)

b = int(sys.argv[2])# 代表當(dāng)前運(yùn)行的程序所提供的第二個(gè)參數(shù)

c = a/b

print("您輸入的兩個(gè)數(shù)相除的結(jié)果是:",c)

except IndexError:

print("索引錯(cuò)誤:運(yùn)行程序時(shí)輸入的參數(shù)個(gè)數(shù)不夠")

except ValueError:

print("數(shù)值錯(cuò)誤:程序只能接受整數(shù)參數(shù)")

except ArithmeticError:

print("算術(shù)錯(cuò)誤")

except Exception:

print("未知異常")

3. 多異常捕獲:指一個(gè)Except塊可以捕獲多種類型的異常。例如:

>>> import sys

>>> try:

a = int(sys.argv[1])

b = int(sys.argv[2])

c = a/b

print("您輸入的兩個(gè)數(shù)相除的結(jié)果是:",c)

except (IndexError, ValueError, ArithmeticError):

print("程序發(fā)生了數(shù)組越界、格式錯(cuò)誤、算術(shù)異常之一")

except: # 此處的省略也是合法的,一般放在最后

print("未知異常")

程序發(fā)生了數(shù)組越界、格式錯(cuò)誤、算術(shù)異常之一

4. 訪問異常信息

1)如果程序需要在except塊中訪問異常對(duì)象的相關(guān)信息,則可以通過為異常對(duì)象聲明變量來實(shí)現(xiàn)。

2)所在的異常對(duì)象都包含如下幾個(gè)對(duì)象和方法:

args: 該屬性返回異常的錯(cuò)誤編號(hào)和描述字符串

errno:該屬性返回異常的錯(cuò)誤編號(hào)

strerror:該屬性返回異常的描述字符串

with_traceback():通過該方法可以處理異常的傳播軌跡

3)程序訪問異常信息實(shí)例:

>>> def foo():

try:

fis = open("a.txt")

except Exception as e:

print(e.args)

print(e.errno)

print(e.strerror)

>>> foo()

#運(yùn)行結(jié)果如下:

(2, 'No such file or directory')

2

No such file or directory

5. else塊

1) 在Python異常處理流程中還可添加一個(gè)else塊,當(dāng)try塊沒有出現(xiàn)異常時(shí),程序會(huì)執(zhí)行else塊。

例如:

>>> s = input("請(qǐng)輸入除數(shù):")

請(qǐng)輸入除數(shù):5

>>> try:

result = 20/int(s)

print('20除以%s的結(jié)果是:%g'%(s,result))

except ValueError:

print('值錯(cuò)誤,您必須輸入數(shù)值')

except ArithmeticError:

print('算術(shù)錯(cuò)誤,您不能輸入0')

else:

print('沒有出現(xiàn)異常')

#運(yùn)行結(jié)果如下:

20除以5的結(jié)果是:4

沒有出現(xiàn)異常

2)實(shí)際上大部分語言異常處理都沒有else塊,可以將else塊的內(nèi)容直接放到try后面。但Python異常處理使用else塊也不是多余的語法。因?yàn)樵趀lse塊中的異常不會(huì)被except捕獲,該異常會(huì)傳給Python解釋器導(dǎo)致程序中止。

3)如果希望某段代碼的異常,能被后面的except捕獲,那么就應(yīng)該放在try塊中;如果不希望被except捕獲,就應(yīng)該放在else塊中。

6. 使用finally回收資源

1)為了能夠保證回收try塊中打開的一些物理資源(如數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接和磁盤文件),異常處理機(jī)制提供了finally塊。

2)Python 完整的異常處理語法結(jié)構(gòu)如下:

try:

? ? #業(yè)務(wù)實(shí)現(xiàn)代碼

except SubException1 as e:

? ? #異常處理塊1

except SubException2 as e:

? ? #異常處理塊2

else:

? ? #正常處理塊

finally:

? ? #資源回收塊


三、使用raise引發(fā)異常

1. 如果程序中數(shù)據(jù)或執(zhí)行與現(xiàn)實(shí)的需求不符,但是系統(tǒng)不會(huì)判斷這樣的異常,只能程序員來決定是否引發(fā)異常,此時(shí)可以使用raise語句來完成這種自行引發(fā)的異常。

2. raise語句三種常見的用法:

1) 單獨(dú)一個(gè)raise,該語句引發(fā)當(dāng)前上下文中捕獲的異常,或默認(rèn)引發(fā)RuntimeError異常。

2)raise后帶一個(gè)異常類,該語句引發(fā)指定的異常類。

3)raise后帶一個(gè)異常對(duì)象,該語句引發(fā)制定的異常對(duì)象。

以上三種最終都是引發(fā)一個(gè)異常實(shí)例,每次只能引發(fā)一個(gè)異常實(shí)例。

3. 用戶引發(fā)異常的兩種方式:raise和except,例如:

>>> def main():

try: # 使用try...except來捕獲異常,此時(shí)出現(xiàn)異常也不會(huì)傳給調(diào)用它的main()函數(shù)

mtd(3)

except Exception as e:

print('程序出現(xiàn)異常類是:',e)

mtd(3) #不使用try...except來捕獲異常,異常會(huì)傳播并導(dǎo)致程序中止

>>> def mtd(a):

if a>0:

raise ValueError("a的值大于0,不符合要求")

>>> main()


四、異常的傳播軌跡

1. 異常只要沒有被完全捕獲,異常就會(huì)從發(fā)生異常的函數(shù)或方法向外傳播,首先傳給該函數(shù)或方法的調(diào)用者,然后,,直到傳給Python解釋器,Python解釋器就會(huì)中止程序,并打印異常傳播的軌跡信息。所以通常我們看到大段的異常信息,并不一定發(fā)生很多嚴(yán)重的問題,可能只是一個(gè)異常引發(fā)的。

2. Python專門提供trackback模塊來處理異常傳播的軌跡,兩種常用的方法是:

1)trackback.print_exc(): 將異常傳播軌跡輸出到控制臺(tái)或文件中。

2) format_exc():將異常傳播軌跡轉(zhuǎn)換成字符串。

五、異常處理規(guī)則

1.? 不要過度使用異常處理,注意以下兩點(diǎn):

1) 需要編寫錯(cuò)誤處理代碼,而不是簡單的用異常來代替處理。

2)? 不能用異常來代替流程控制。

2.? 不要使用過度龐大的try塊:try塊越大,發(fā)生異常的可能性就越大。而且在龐大的try塊后勢必有大量的except塊,這時(shí)要判斷各個(gè)塊之間的邏輯關(guān)系,編寫程序會(huì)變得更加復(fù)雜。

3. 不要忽略捕捉到的異常: 程序應(yīng)該盡量修改異常,保證程序繼續(xù)運(yùn)行;不要將本層的異常傳給上一層去處理。

六、本節(jié)回顧

1. 你怎么理解異常處理機(jī)制?

2. Python異常處理的5個(gè)關(guān)鍵字是什么?(try\ except\else\finally\raise)

3. 如何使用raise引發(fā)異常?

4. 如何獲得異常的源頭和軌跡?

5.異常處理的原則有哪些?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容