一法精、概要
? 一個程序即使沒有任何語法錯誤多律,即使解題的邏輯也正確,在執(zhí)行的時候仍然可能出現(xiàn) 各種“運行時錯誤”搂蜓,導(dǎo)致程序無法按照預(yù)定的步驟順利執(zhí)行狼荞、正常結(jié)束。其后果是要么由系 統(tǒng)強(qiáng)行中止程序的運行帮碰,要么程序帶著錯誤繼續(xù)運行而得出錯誤的結(jié)果相味。這類運行時錯誤稱 為異常或例外(exception)收毫。產(chǎn)生異常的原因是復(fù)雜而多樣的攻走,既有程序設(shè)計的問題,也有運行環(huán)境的問題此再,如除數(shù)為零昔搂、用戶輸入數(shù)據(jù)的類型或個數(shù)不對、列表索引越界,寫入文件的時候输拇,磁盤滿了摘符,或者從網(wǎng)絡(luò)抓取數(shù)據(jù),網(wǎng)絡(luò)突然斷掉了等等策吠。
如果一個程序很容易受到異常的影響而崩潰(即中止執(zhí)行)逛裤,那就不是好的程序,因為程序崩潰意味著無法完成預(yù)定的計算猴抹,不能滿足用戶的需求带族。另外,程序崩潰時系統(tǒng)一般會輸出一堆錯誤消息蟀给,這些消息對程序員來說沒啥大不了蝙砌,但對普通用戶來說則是難以理解的一 堆技術(shù)術(shù)語。用戶不知道發(fā)生了什么跋理,也不知道該如何處理择克。
? 因此,程序員必須在程序中加入處理錯誤的代碼前普,以便在發(fā)生錯誤的情況下能自己處理 錯誤肚邢,使程序錯誤對用戶是不可見的。這樣的程序在發(fā)生錯誤的情況下也能正常結(jié)束而非崩 潰拭卿,并且顯示給用戶的也是可理解的友好的信息骡湖。我們稱這樣的程序是健壯的(robust)
? 程序開發(fā)時贱纠,很難將所有的特殊情況都處理的面面俱到,通過異常捕獲可以針對突發(fā)事件做集中的處理响蕴,從而保證程序的穩(wěn)定性和健壯性
二并巍、為什么要使用異常
- 錯誤處理,當(dāng)python檢查程序運行時的錯誤就引發(fā)異常换途,你可以在程序里捕捉和處理這些錯誤懊渡,或者忽略它們。
- 事件通知军拟,異常也可以作為某種條件的信號剃执,而不需要在程序里傳送結(jié)果標(biāo)志或顯式地測試它們。
- 特殊情形處理懈息,有時有些情況是很少發(fā)生的肾档,把相應(yīng)的處理代碼改為異常處理會更好一些。
- 奇特的控制流辫继,異常是一個高層次的"goto"怒见,可以把它作為實現(xiàn)奇特的控制流的基礎(chǔ)。如反向跟蹤等姑宽。
二遣耍、異常處理
1、概要
程序運行時如果發(fā)生錯誤炮车,就“拋出”一個異常舵变,而系統(tǒng)能夠“捕獲”這個異常并執(zhí)行特定的異常處理代碼,即使一條語句或表達(dá)式在語法上是正確的瘦穆,當(dāng)試圖執(zhí)行它時也可能會引發(fā)錯誤纪隙。運行期檢測到的錯誤稱為異常,并且程序不會無條件的崩潰,
2扛或、捕獲異常
-
語法格式
try: <語句塊> except 異常類名 as err: <異常處理語句塊>
-
解釋
- 執(zhí)行<語句塊>绵咱,如果一切正常,執(zhí)行結(jié)束后控制轉(zhuǎn)向 try-except 的下一條語句
- 如果執(zhí)行過程中發(fā)生了異常熙兔,則控制轉(zhuǎn)向異常處理語句塊悲伶,執(zhí)行結(jié)束后控制轉(zhuǎn)向 try-except 的下一條語句。
-
舉個栗子
try name = 'laowang' print(name[7]) except: print('索引越界') ''' IndexError: string index out of range '''
索引越界錯誤發(fā)生之后黔姜,控制自動轉(zhuǎn)到 except 子句下面的處理代碼拢切,處理完畢還可以繼 續(xù)執(zhí)行程序的其他語句蒂萎。如果沒有錯誤秆吵,則忽略 except 部分。
-
優(yōu)點
使用異常處理機(jī)制可以使程序的核心算法代碼與錯誤處理代碼相互分離五慈,從而保持程序結(jié)構(gòu)的清晰纳寂。如果要了解程序的主要算法主穗,只需讀 try 下面的語句塊,例如
try: do_something1() do_something2() do_something3() except: do_error()
顯然這種形式的代碼既能保持算法邏輯的清晰完整,又能實現(xiàn)錯誤檢測,
3毙芜、捕獲多個異常
-
概要
以上用到的簡單形式的 try 語句不加區(qū)分地對所有錯誤進(jìn)行相同的處理忽媒,如果需要對不同錯誤類型進(jìn)行不同的處理,則可使用更精細(xì)的控制
-
語法格式
try: <語句> #運行核心代碼 except <名字>: <語句> #如果在try部份引發(fā)了'name'異常 except <名字>腋粥,<數(shù)據(jù)>: <語句> #如果引發(fā)了'name'異常晦雨,獲得附加的數(shù)據(jù) else: <語句> #如果沒有異常發(fā)生
-
解釋
執(zhí)行<語句塊>,如果一切正常隘冲,執(zhí)行結(jié)束后控制轉(zhuǎn)向 try-except 的下一條語句闹瞧;
如果執(zhí)行過程中發(fā)生了異常,則系統(tǒng)依次檢查各個 except 子句試圖找到與所發(fā)生的異常相匹 配的錯誤類型展辞。
如果找到奥邮,就執(zhí)行相應(yīng)的異常處理語句塊,
如果找不到則執(zhí)行最后一個 except 子句下的缺省異常處理語句塊罗珍。
異常處理結(jié)束后控制轉(zhuǎn)到 try-except 的下一條語句洽腺。
-
注意: 最后一個不含錯誤類型的 except 子句是可選的,用于捕獲所有未預(yù)料到的錯誤類型覆旱。如果未
使用最后這個 except 子句蘸朋,那么當(dāng)異常與所有錯誤類型都不匹配時,則由 Python 解釋器捕獲 異常并處理之
-
舉個栗子
""" 異常類型:當(dāng)python解釋器拋出異常時扣唱,最后一行錯誤信息的第一個單詞度液,就是錯誤類型 """ try: # 提示用戶輸入一個整數(shù) num = int(input("請輸入一個整數(shù):")) # 使用10除以該數(shù)字并輸出 result = 10 / num print(result) except ZeroDivisionError: print("計算機(jī)不能除零") except ValueError: print("您輸入的不是整數(shù)。画舌。") print("程序正常結(jié)束:over堕担。。")
4曲聂、finally
-
說明
python總會執(zhí)行finally子句霹购,無論try子句執(zhí)行時是否發(fā)一異常。
-
語法格式
try: # 可能產(chǎn)生異常的代碼 pass except 異常類型 as result: # 產(chǎn)生異常朋腋,執(zhí)行此處 print('產(chǎn)生錯誤了:%s'% result) finally: # 無論是否產(chǎn)生異常齐疙,此處的代碼一定會被執(zhí)行 print('真高興')
-
解釋
- 如果沒有發(fā)生異常,python運行try子句旭咽,然后是finally子句贞奋,然后繼續(xù)。
- 如果在try子句發(fā)生了異常穷绵,python就會回來執(zhí)行finally子句轿塔,然后把異常遞交給上層try,控制流不會通過整個try語句
-
舉個栗子
5、raise
-
說明
允許程序員強(qiáng)制拋出一個指定的異常
-
語法格式
raise <name> raise <name>,<data> raise
-
舉個栗子
raise NameError('手動拋出異常') def thorw_err(): raise Exception("拋出一個異常") def throw_error(): raise Exception("拋出一個異常") #異常被拋出勾缭,print函數(shù)無法執(zhí)行 print("大吉大利!今晚吃雞!")
如果你需要明確一個異常是否拋出揍障,但不想處理它,raise 語句可以讓你很簡單的重新拋出該異常
try: raise NameError('手動拋出異常') except NameError: print('不好了!不好了!出BUG了') raise
-
注意事項
要拋出的異常由 raise的唯一參數(shù)標(biāo)識俩由。它必需是一個異常實例或異常類(繼承自 Exception的類)毒嫡。
6、完整的異常
-
說明
在實際開發(fā)中幻梯,為了能夠處理復(fù)雜的異常情況兜畸,要使用完整的異常語法。
-
語法格式
try: # 嘗試執(zhí)行的代碼 pass except 錯誤類型1: # 針對于錯誤類型1碘梢,對應(yīng)的的代碼處理 pass except (錯誤類型2, 錯誤類型3): # 針對于錯誤類型2 和 3膳叨,對應(yīng)的的代碼處理 pass except Exception as result: # 打印錯誤信息,未知類型異常 print(result) else: # 沒有異常才會執(zhí)行的代碼 pass finally: # 無論是否有異常,都會執(zhí)行的代碼 print("無論是否有異常痘系,此處代碼都會執(zhí)行")
-
舉個栗子
try: f = open("testfile", "w") f.write("用于測試異常!!") except IOError: print("Error: 沒有找到文件或讀取文件失敗") else: print (內(nèi)容寫入文件成功") finally: f.close()
三菲嘴、用戶自定義異常
1、概括
有些時候我們需要創(chuàng)建自己的異常類汰翠,這個類就像其他類一樣龄坪,只要是確保從Exception類繼承
不管是間接繼承還是直接繼承,也就是是繼承其他內(nèi)建異常類也是可以的
2复唤、自定義異常
-
寫一個類繼承Exception類
class NetworkError(Exception): pass
-
或者繼承他的子類
class HostnameError(NetworkError): pass class TimeoutError(NetworkError): pass class ProtocolError(NetworkError): pass
-
說明
自定義異常類應(yīng)該總是繼承自內(nèi)置的
Exception
類健田, 或者是繼承自那些本身就是從Exception
繼承而來的類。 盡管所有類同時也繼承自BaseException
佛纫,但你不應(yīng)該使用這個基類來定義新的異常妓局。BaseException
是為系統(tǒng)退出異常而保留的,比如KeyboardInterrupt
或SystemExit
以及其他那些會給應(yīng)用發(fā)送信號而退出的異常呈宇。 因此好爬,捕獲這些異常本身沒什么意義。 這樣的話甥啄,假如你繼承BaseException
可能會導(dǎo)致你的自定義異常不會被捕獲而直接發(fā)送信號退出程序運行存炮。
四、異常的傳遞
1蜈漓、異常的嵌套
如果try嵌套穆桂,那么如果里面的try沒有捕獲到這個異常,那么外面的try會接收到這個異常融虽,然后進(jìn)行處理享完,如果外邊的try依然沒有捕獲到,那么再進(jìn)行傳遞有额。般又。彼绷。
2、異常的傳遞
- 說明
異常的傳遞倒源,當(dāng)函數(shù)執(zhí)行出現(xiàn)異常,會將異常傳遞給函數(shù)的調(diào)用方
如果傳遞到主程序句狼,仍然沒有異常處理笋熬,程序才會被終止
詳細(xì)描述:如果一個異常是在一個函數(shù)中產(chǎn)生的,例如函數(shù)A---->函數(shù)B---->函數(shù)C,而異常是在函數(shù)C中產(chǎn)生的腻菇,那么如果函數(shù)C中沒有對這個異常進(jìn)行處理胳螟,那么這個異常會傳遞到函數(shù)B中,如果函數(shù)B有異常處理那么就會按照函數(shù)B的處理方式進(jìn)行執(zhí)行筹吐;如果函數(shù)B也沒有異常處理糖耸,那么這個異常會繼續(xù)傳遞,以此類推丘薛。嘉竟。。如果所有的函數(shù)都沒有處理洋侨,那么此時就會進(jìn)行異常的默認(rèn)處理舍扰,即通常見到的那樣
提示:
- 在開發(fā)中,可在主函數(shù)中增加異常捕獲
- 而在主函數(shù)中調(diào)用的其他函數(shù)希坚,只要出現(xiàn)異常边苹,都會傳遞到主函數(shù)的異常捕獲中
- 這樣就不需要在代碼中,增加大量的異常捕獲裁僧,能夠保證代碼的整潔
五个束、總結(jié)
1、使用原則
大致來說聊疲,Python的異常在使用上都很簡單茬底。異常背后真正的技巧在于確定except分句要具體或多通用,以及try語句中要包括多少代碼获洲。
-
try語句中要包括多少代碼桩警。
簡要原則,經(jīng)常會失敗的運算一般都應(yīng)該包裝在try語句內(nèi)。例如:和系統(tǒng)狀態(tài)銜接的運算(文件開啟昌妹,套接字調(diào)用等等)就是try的主要候選者捶枢。在簡單的腳本中,你會希望這類運算失敗時終止程序飞崖,而不是被捕捉或被忽略烂叔。如果是一個重大的錯誤,更應(yīng)如此固歪。Python的錯誤會產(chǎn)生有用的錯誤信息蒜鸡,而且這通常就是所期望的最好的結(jié)果胯努。應(yīng)該try/finally中實現(xiàn)終止動作无埃,從而保證它們的執(zhí)行签杈。這個語句形式可執(zhí)行代碼昔头,無論異常是否發(fā)生室琢。偶爾闹炉,把對大型函數(shù)的調(diào)用包裝在單個try語句內(nèi)禽最,而不是讓函數(shù)本身零散著放入若干try的語句中赁濒。這樣會更方便为鳄。這樣的話局嘁,函數(shù)中的異常就會往上傳遞到調(diào)用周圍的try溉箕,而你也可以減少函數(shù)中的代碼量。
-
避免空except語句
如果使用空except語句,可能攔截到異常嵌套結(jié)構(gòu)中較高層的try處理器所期待的事件這類代碼可能會捕捉無關(guān)的系統(tǒng)異常悦昵。如內(nèi)存錯誤肴茄,程序錯誤,迭代停止以及系統(tǒng)推出等等但指,都會在Python中引發(fā)異常寡痰。這里異常通常是不應(yīng)該攔截的。
捕捉太少:使用基于類的分類
2棋凳、注意事項
一個try就有一個except或多個except氓癌,不要只捕獲不處理
-
慎用異常:
- 找到python的內(nèi)置異常
- 理解python的內(nèi)置異常分別對應(yīng)什么情況
- 閱讀你的代碼,找到你的代碼里可能會拋出內(nèi)置異常的地方
- 僅對這幾行代碼做異常處理
- 假設(shè)你無法知道你的代碼會拋出什么異常贫橙,那么你的異常處理便是無效的
3贪婉、開發(fā)建議
- 實現(xiàn)異常類,比較好的做法是:將所有自定義異常放在一個單獨的文件中(例如:
exceptions.py
或errors.py
)卢肃,許多標(biāo)準(zhǔn)模塊也都是這樣做的疲迂。 - 既然自定義異常是類,那么它必然可以實現(xiàn)一個普通類能做的所有事情莫湘。但一般而言尤蒿,應(yīng)該盡量保持簡單、簡潔幅垮。大多數(shù)實現(xiàn)都是聲明一個自定義基類腰池,并從這個基類派生出其他的(由程序引發(fā)的)異常類。這是 Python 中實現(xiàn)自定義異常的標(biāo)準(zhǔn)方法忙芒,但并不僅限于這種方式示弓。
六、附:內(nèi)建異常體系
-
繼承結(jié)構(gòu)
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StandardError | +-- BufferError | +-- ArithmeticError | | +-- FloatingPointError | | +-- OverflowError | | +-- ZeroDivisionError | +-- AssertionError | +-- AttributeError | +-- EnvironmentError | | +-- IOError | | +-- OSError | | +-- WindowsError (Windows) | | +-- VMSError (VMS) | +-- EOFError | +-- ImportError | +-- LookupError | | +-- IndexError | | +-- KeyError | +-- MemoryError | +-- NameError | | +-- UnboundLocalError | +-- ReferenceError | +-- RuntimeError | | +-- NotImplementedError | +-- SyntaxError | | +-- IndentationError | | +-- TabError | +-- SystemError | +-- TypeError | +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning
-
內(nèi)置異常說明
**異常名稱****** **描述****** BaseException 所有異常的基類 SystemExit 解釋器請求退出 KeyboardInterrupt 用戶中斷執(zhí)行(通常是輸入^C) Exception 常規(guī)錯誤的基類 StopIteration 迭代器沒有更多的值 GeneratorExit 生成器(generator)發(fā)生異常來通知退出 StandardError 所有的內(nèi)建標(biāo)準(zhǔn)異常的基類 ArithmeticError 所有數(shù)值計算錯誤的基類 FloatingPointError 浮點計算錯誤 OverflowError 數(shù)值運算超出最大限制 ZeroDivisionError 除(或取模)零 (所有數(shù)據(jù)類型) AssertionError 斷言語句失敗 AttributeError 對象沒有這個屬性 EOFError 沒有內(nèi)建輸入,到達(dá)EOF 標(biāo)記 EnvironmentError 操作系統(tǒng)錯誤的基類 IOError 輸入/輸出操作失敗 OSError 操作系統(tǒng)錯誤 WindowsError 系統(tǒng)調(diào)用失敗 ImportError 導(dǎo)入模塊/對象失敗 LookupError 無效數(shù)據(jù)查詢的基類 IndexError 序列中沒有此索引(index) KeyError 映射中沒有這個鍵 MemoryError 內(nèi)存溢出錯誤(對于Python 解釋器不是致命的) NameError 未聲明/初始化對象 (沒有屬性) UnboundLocalError 訪問未初始化的本地變量 ReferenceError 弱引用(Weak reference)試圖訪問已經(jīng)垃圾回收了的對象 RuntimeError 一般的運行時錯誤 NotImplementedError 尚未實現(xiàn)的方法 SyntaxError Python 語法錯誤 IndentationError 縮進(jìn)錯誤 TabError Tab 和空格混用 SystemError 一般的解釋器系統(tǒng)錯誤 TypeError 對類型無效的操作 ValueError 傳入無效的參數(shù) UnicodeError Unicode 相關(guān)的錯誤 UnicodeDecodeError Unicode 解碼時的錯誤 UnicodeEncodeError Unicode 編碼時錯誤 UnicodeTranslateError Unicode 轉(zhuǎn)換時錯誤 Warning 警告的基類 DeprecationWarning 關(guān)于被棄用的特征的警告 FutureWarning 關(guān)于構(gòu)造將來語義會有改變的警告 OverflowWarning 舊的關(guān)于自動提升為長整型(long)的警告 PendingDeprecationWarning 關(guān)于特性將會被廢棄的警告 RuntimeWarning 可疑的運行時行為(runtime behavior)的警告 SyntaxWarning 可疑的語法的警告 UserWarning 用戶代碼生成的警告