一氮凝、錯誤和異常
1、錯誤
從軟件方面來講罩阵,錯誤通常是語法或邏輯上的竿秆。語法錯誤會導(dǎo)致程序代碼不能被解釋器解釋稿壁,這些錯誤必須在程序執(zhí)行前糾正幽钢。當程序的語法正確后,剩下的就是邏輯錯誤了傅是。邏輯錯誤可能是由于不完整或是不合法的代碼邏輯所致匪燕,還可能是由于代碼邏輯無法生成或執(zhí)行。
編譯時會檢查語法錯誤喧笔,編譯完成后 Python 解釋器會在程序運行時檢測邏輯錯誤帽驯。當檢測到一個錯誤,Python 解釋器就引發(fā)一個異常书闸,并顯示異常的詳細信息尼变。程序員可以根據(jù)這些信息迅速定位問題并進行調(diào)試。
2浆劲、異常
異常處理通常分為兩個階段:首先是解釋器在執(zhí)行代碼過程中發(fā)生錯誤并引起異常嫌术,異常會在錯誤檢測到的地方引發(fā),并將當前流打斷梳侨,然后是檢測異常并執(zhí)行相關(guān)的處理程序蛉威。
異常引發(fā)后日丹,可以調(diào)用很多不同的操作走哺。可以是忽略錯誤(記錄錯誤但不采取任何措施)哲虾,或是減輕問題的影響后設(shè)法繼續(xù)執(zhí)行程序丙躏。所有的這些操作都代表一種程序的繼續(xù)择示,關(guān)鍵是程序員在錯誤發(fā)生時可以指示程序如何執(zhí)行。
類似 Python 這樣支持引發(fā)和處理異常(這更重要)的語言晒旅,可以讓開發(fā)人員可以在錯誤發(fā)生時更直接地控制它們栅盲。程序員不僅僅有了檢測錯誤的能力,還可以在它們發(fā)生時采取更可靠的補救措施废恋。由于有了運行時管理錯誤的能力谈秫,應(yīng)用程序的健壯性有了很大的提高。
常見的異常情況
NameError 表示我們訪問了一個沒有初始化的變量鱼鼓。
任何數(shù)值被零除都會導(dǎo)致一個 ZeroDivisionError 異常拟烫。
SyntaxError 異常是語法錯誤,是唯一不是在運行時發(fā)生的異常迄本。它代表 Python 代碼中有一個不正確的語法結(jié)構(gòu)硕淑,在它改正之前程序無法執(zhí)行。這些錯誤一般都是在編譯時發(fā)生嘉赎,Python 解釋器無法把你的腳本轉(zhuǎn)化為 Python 字節(jié)代碼置媳,當然這也可能是你導(dǎo)入一個有缺陷的模塊的時候。
IndexError 異常在你嘗試使用一個超出范圍的值來索引序列時引發(fā)公条。
映射對象拇囊,例如字典,是依靠關(guān)鍵字(keys)訪問數(shù)據(jù)值的靶橱。如果使用錯誤的或是不存在的鍵請求字典就會引發(fā)一個 KeyError 異常寂拆。
類似嘗試打開一個不存在的磁盤文件一類的操作會引發(fā)一個 FileNotFoundError 異常。
屬性和方法被定義后抓韩,我們可以使用熟悉的點操作符訪問它纠永,但如果是沒有定義的屬性,將導(dǎo)致一個 AttributeError 異常谒拴。
二尝江、try 語句
要給你的代碼添加錯誤檢測及異常處理,只要將它們封裝在 try-except 語句當中英上。 try 子句的代碼塊炭序,就是你打算監(jiān)測的代碼,檢查有無異常發(fā)生苍日。except 句子的代碼塊惭聂,則是你處理異常的代碼。
在程序運行時相恃,解釋器嘗試執(zhí)行 try 子句塊里的所有代碼辜纲,如果代碼塊完成后沒有異常發(fā)生,就會忽略 except 子句繼續(xù)執(zhí)行。當 try 子句塊中發(fā)生異常時耕腾,Python 解釋器會依次檢查 except 子句见剩,直到找到與異常匹配的子句,并執(zhí)行其中的代碼塊扫俺。
當找到一個匹配的 except 子句時苍苞,異常就被賦給 except 子句中 as 關(guān)鍵字后面的目標,然后執(zhí)行 excep 子句的 suite狼纬。所有 except 子句必須有一個可執(zhí)行塊羹呵。
try 語句塊中異常發(fā)生點后的剩余語句永遠不會執(zhí)行。 一旦一個異常被引發(fā)疗琉,就必須決定控制流下一步到達的位置担巩。try子句代碼塊中的剩余代碼將被忽略,解釋器將搜索處理器没炒,一旦找到涛癌,就開始執(zhí)行處理器中的代碼。
如果沒有找到合適的處理器送火,那么異常就向上移交給調(diào)用者去處理拳话。如果在上層調(diào)用者也沒找到對應(yīng)處理器,該異常會繼續(xù)被向上移交种吸,直到找到合適處理器弃衍。如果到達最頂層仍然沒有找到對應(yīng)處理器,那么就認為這個異常是未處理的坚俗,Python 解釋器會顯示出跟蹤返回消息镜盯,然后退出。
如果同一異常存在兩個嵌套處理程序猖败,并且內(nèi)部處理程序的 try 子句中發(fā)生異常速缆,則外部處理程序?qū)⒉惶幚懋惓!H绻?except 子句頭部的一個表達式引發(fā)了異常恩闻, 那么就會中斷原異常處理器的搜索艺糜, 而在外層代碼和調(diào)用棧上搜索新的異常處理器(就好像是整個 try 語句發(fā)生了異常一樣)。
1幢尚、try-except 語句
我們使用 try-except 語句進行異常的監(jiān)控破停,并且對監(jiān)測到的異常進行處理。最常見的 try-except 語句語法如下所示尉剩。它由 try 子句和 except 子句組成真慢,也可以有一個可選的 as 語句來保存錯誤原因。
try:
try_suite # 監(jiān)控這里的異常
except Exception[as reason]:
except_suite # 異常處理代碼
2理茎、處理多個異常的 except 語句
我們可以在一個 except 子句里指定并處理多個異常黑界。except 語句在指定多個異常時要求異常被放在一個元組里:
try:
try_suite
except (Exception1, Exception2)[as reason]:
suite_for_Exception1_and_Exception2
3管嬉、帶有多個 except 的 try 語句
你可以把多個 except 語句連接在一起,處理一個 try 塊中可能發(fā)生的多種異常园爷,我們可以分別為每個異常類型分別創(chuàng)建對應(yīng)處理程序,這樣就可以更加細致的處理錯誤并得到更詳細的錯誤信息式撼。
try:
try_suite
except Exception1[as reason1]:
suite_for_exception_Exception1
except Exception2[as reason2]:
suite_for_exception_Exception2
同樣童社,首先嘗試執(zhí)行 try 子句,如果沒有錯誤著隆,忽略所有的 except 從句繼續(xù)執(zhí)行扰楼。如果發(fā)生異常,解釋器將在這一串處理器(except 子句)中從上到下查找匹配的異常美浦。如果找到對應(yīng)的處理器弦赖,執(zhí)行流將跳轉(zhuǎn)到這里。
4浦辨、捕獲所有異常
如果查詢異常類繼承的樹結(jié)構(gòu)蹬竖,我們會發(fā)現(xiàn) Exception 類是在最頂層的,所以我們可以在 except 子句中通過指定 Exception 異常來捕獲所有的異常:
try:
pass
except Exception [as e]:
# error occurred, log 'e', etc.
另一個我們不太推薦的方法是使用 裸 except 子句來匹配所有的異常:
try:
pass
except:
# error occurred, etc.
雖然這樣的代碼可以捕獲大多異常流酬,但它不是好的 Python 編程樣式币厕。它會捕獲所有異常,你可能會忽略掉重要的錯誤芽腾,正常情況下這些錯誤應(yīng)該讓調(diào)用者知道并做一定處理旦装。通常我們只是在多個 except 組合子句的最后的 except 子句中來捕捉 Exception 異常。
Python 提供給程序員的 try-except 語句是為了更好地跟蹤潛在的錯誤并在代碼里準備好處理異常的邏輯摊滔。它的目的是減少程序出錯的次數(shù)并在出錯后仍能保證程序正常執(zhí)行阴绢。
5、異常參數(shù)
異常也可以有參數(shù)艰躺,當異常被引發(fā)后參數(shù)是作為附加幫助信息傳遞給異常處理器的呻袭。標準內(nèi)建異常提供至少一個參數(shù),指示異常原因的一個字符串腺兴。
異常的參數(shù)可以在處理器里忽略棒妨,但 Python 中可以在 except 關(guān)鍵字的后面使用過 as 關(guān)鍵字來保存這個值。我們已經(jīng)在上邊接觸到相關(guān)內(nèi)容:要想訪問提供的異常原因含长,你必須保留一個變量來保存這個參數(shù)券腔。把這個參數(shù)放在 as 語句的后面批钠,并接在 except 語句要處理的異常后面啡省。except 語句的這個語法可以被擴展為:
# single exception
except Exception[as reason]:
suite_for_Exception_with_Argument
# multiple exceptions
except (Exception1, Exception2, ..., ExceptionN)[as reason]:
suite_for_Exception1_to_ExceptionN_with_Argument
reason 將會是一個包含來自導(dǎo)致異常的代碼的診斷信息的類實例栋猖。異常參數(shù)自身會組成一個元組躬贡,并存儲為類實例(異常類的實例)的屬性姨涡。上邊的第一種用法中幌蚊,reason 將會是一個 Exception 類的實例翻伺。
對于大多內(nèi)建異常畴蒲,也就是從 StandardError 派生的異常,這個元組只包含一個指示錯誤原因的字符串染簇。一般說來参滴,異常的名字已經(jīng)是一個滿意的線索了,但這個錯誤字符串會提供更多的信息锻弓。操作系統(tǒng)或其他環(huán)境類型的錯誤砾赔,例如 IOError ,元組中會把操作系統(tǒng)的錯誤編號放在錯誤字符串前青灼。
6暴心、else 子句
我們已經(jīng)看過 else 語句段配合其他的 Python 語句,比如條件和循環(huán)杂拨。至于 try-except 語句专普,它的功能和你所見過的其他 else 語句沒有太多的不同:在 try 語句范圍中沒有異常被檢測到時,執(zhí)行 else 子句弹沽。
在 else 子句范圍中的任何代碼運行前檀夹,try 范圍中的所有代碼必須完全執(zhí)行成功(也就是,結(jié)束前沒有引發(fā)異常)策橘。
try:
pass
except Exception[as reason]:
pass
else:
pass
7击胜、finally 子句
finally 子句是無論異常是否發(fā)生、是否捕捉都會執(zhí)行的一段代碼役纹。你可以將 finally 子句僅僅配合 try 子句一起使用偶摔,也可以和 try-except(else 也是可選的,但是必須和 except 子句配合使用)一起使用促脉。
try:
pass
except Exception[as reason]:
pass
else:
pass
finally:
pass
另一種使用 finally 的方式是 finally 單獨和 try 連用辰斋。
try:
pass
finally:
pass
當在 try 子句范圍中產(chǎn)生一個異常時,會立即跳轉(zhuǎn)到 finally 語句段瘸味。當 finally 中的所有代碼都執(zhí)行完畢后宫仗,會繼續(xù)向上一層引發(fā)異常。
三旁仿、觸發(fā)異常
到目前為止藕夫,我們所見到的異常都是由解釋器引發(fā)的,由于執(zhí)行期間的錯誤而引發(fā)枯冈。程序員在編寫 API 時可能希望手動觸發(fā)異常毅贮,為此 Python 提供了一種機制可以讓程序員明確的觸發(fā)異常,就是 raise 語句尘奏。
1滩褥、raise 語句
rasie 一般的用法是:
raise [SomeException [, args [, traceback]]]
第一個參數(shù) SomeExcpetion 是觸發(fā)異常的名字。如果有炫加,它必須是一個異常的類或?qū)嵗寮澹绻衅渌麉?shù)(args 或 traceback)铺然,則必須提供 SomeExcpetion。
第二個符號為可選的 args(比如參數(shù)酒甸,值)來傳給異常魄健。這可以是一個單獨的對象也可以是一個對象的元組。如果 args 原本就是元組插勤,那么就將其傳給異常去處理沽瘦;如果 args 是一個單獨的對象,生成只有一個元素的元組(就是單元素元組)饮六。大多數(shù)情況下其垄,使用單一的字符串用來指示錯誤的原因苛蒲。如果傳的是元組卤橄,通常的組成是一個錯誤字符串,一個錯誤編號臂外,可能還有一個錯誤的地址窟扑,比如文件,等等漏健。
最后一項參數(shù)嚎货,traceback,同樣是可選的(實際上很少用它)蔫浆,如果有的話殖属,則是當異常觸發(fā)時新生成的一個用于異常-正常化(exception—normally)的追蹤(traceback)對象瓦盛。當你想重新引發(fā)異常時洗显,第三個參數(shù)很有用(可以用來區(qū)分先前和當前的位置)。如果沒有這個參數(shù)原环,就填寫 None挠唆。
最常見的用法為 SomeException 是一個類。不需要其他的參數(shù)嘱吗,但如果有的話玄组,可以是一個單一對象參數(shù),一個參數(shù)的元組谒麦,或一個異常類的實例俄讹。如果參數(shù)是一個實例,可以由給出的類及其派生類實例化(已存在異常類的子集)绕德。若參數(shù)為實例颅悉,則不能有更多的其他參數(shù)。
2迁匠、斷言
斷言是一句必須等價于布爾真的判定剩瓶;此外驹溃,發(fā)生異常也意味著表達式為假。斷言可以簡簡單單的想象為raise-if-not 語句延曙。測試一個表達式,如果返回值是假,觸發(fā)異常豌鹤。如果斷言成功不采取任何措施(類似語句),否則觸發(fā) AssertionError (斷言錯誤)的異常枝缔。
assert expression[, arguments]
AssertionError 異常和其他的異常一樣可以用 try-except 語句塊捕捉布疙,但是如果沒有捕捉,它將終止程序運行而且提供一個如上的 traceback愿卸。
下面是我們?nèi)绾斡?try-except 語句捕獲 AssertionError 異常:《Python基礎(chǔ)手冊》系列:
Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復(fù)合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包