第八章 异常
第八章 異常
異常事件可能是錯(cuò)誤(如試圖除以零),也可能是通常不會(huì)發(fā)生的事情。
Python提供功能強(qiáng)大的替代解決方案——異常處理機(jī)制。
異常是什么?
Python使用異常對(duì)象來表示異常狀態(tài),并在遇到錯(cuò)誤時(shí)引發(fā)異常。異常對(duì)象未被處理或捕 獲)時(shí),程序?qū)⒔K止并顯示一條錯(cuò)誤消息(traceback)。
1 / 0 ''' ZeroDivisionError Traceback (most recent call last) <ipython-input-146-05c9758a9c21> in <module>() ----> 1/0 ZeroDivisionError: division by zero '''事實(shí)上,每個(gè)異常都是某個(gè)類(這里是ZeroDivisionError)的實(shí)例.
讓事情沿你指定的軌道出錯(cuò)
raise 語句
要引發(fā)異常,可使用raise語句,并將一個(gè)類(必須是Exception的子類)或?qū)嵗鳛閰?shù)。將類作為參數(shù)時(shí),將自動(dòng)創(chuàng)建一個(gè)實(shí)例。
在第一個(gè)示例(raise Exception)中,引發(fā)的是通用異常,沒有指出出現(xiàn)了什么錯(cuò)誤。
在第二個(gè)示例中,添加了錯(cuò)誤消息beyondhuangjiaju。
一些內(nèi)置的異常類如下表所示:
| Exception | 幾乎所有的異常類都是從它派生而來的 |
| AttributeError | 引用屬性或給它賦值失敗時(shí)引發(fā) |
| OSError | 操作系統(tǒng)不能執(zhí)行指定的任務(wù)(如打開文件)時(shí)引發(fā),有多個(gè)子類 |
| IndexError | 使用序列中不存在的索引時(shí)引發(fā),為L(zhǎng)ookupError的子類 |
| KeyError | 使用映射中不存在的鍵時(shí)引發(fā),為L(zhǎng)ookupError的子類 |
| NameError | 找不到名稱(變量)時(shí)引發(fā) |
| SyntaxError | 代碼不正確時(shí)引發(fā) |
| TypeError | 將內(nèi)置操作或函數(shù)用于類型不正確的對(duì)象時(shí)引發(fā) |
| ValueError | 將內(nèi)置操作或函數(shù)用于這樣的對(duì)象時(shí)引發(fā):其類型正確但包含的值不合適 |
| ZeroDivisionError | 在除法或求模運(yùn)算的第二個(gè)參數(shù)為零時(shí)引發(fā) |
自定義的異常類
如何創(chuàng)建異常類呢?
就像創(chuàng)建其他類一樣,但務(wù)必直接或間接地繼承Exception(這意味著從任何內(nèi)置異常類派生都可以)。
當(dāng)然,如果你愿意,也可在自定義異常類中添加方法。
捕獲異常
異常比較有趣的地方是可對(duì)其進(jìn)行處理,通常稱之為捕獲異常。
可使用try/except語句。
不用提供參數(shù)
來看一個(gè)能夠“抑制”異常ZeroDivisionError的計(jì)算器類。如果啟用了這種功能,計(jì)算器將打印一條錯(cuò)誤消息,而不讓異常繼續(xù)傳播。在與用戶交互的會(huì)話中使用這個(gè)計(jì)算器時(shí),抑制異常很有用;但在程序內(nèi)部使用時(shí),引發(fā)異常是更佳的選擇(此時(shí)應(yīng)關(guān)閉“抑制”功能)。
class MuffledCalculator: muffled = False def calc(self, expr): try: return eval(expr) except ZeroDivisionError: if self.muffled: print('Division by zero is illegal') else: raisecalculator = MuffledCalculator() calculator.calc('10 / 2')#結(jié)果為:5.0 calculator.calc('10 / 0')#報(bào)錯(cuò)!!!calculator.muffled = True calculator.calc('10 / 0') #結(jié)果為:Division by zero is illegal如果無法處理異常,在except子句中使用不帶參數(shù)的raise通常是不錯(cuò)的選擇,但有時(shí)你可能想引發(fā)別的異常。
在這種情況下,導(dǎo)致進(jìn)入except子句的異常將被作為異常上下文存儲(chǔ)起來,并出現(xiàn)在最終的錯(cuò)誤消息中。
多個(gè) except 子句
當(dāng)你在輸入字符串的時(shí)候,此時(shí)會(huì)引發(fā)另一種異常,該程序中的except子句只捕獲ZeroDivisionError異常,這種異常將成為漏網(wǎng)之魚,導(dǎo)致程序終止。
try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y) except ZeroDivisionError: print("The second number can't be zero!")''' Enter the first number: 1999 Enter the second number: beyond --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-2-f93dbf72378b> in <module>()1 try:2 x = int(input('Enter the first number: ')) ----> 3 y = int(input('Enter the second number: '))4 print(x / y)5 except ZeroDivisionError:ValueError: invalid literal for int() with base 10: 'beyond' '''為同時(shí)捕獲這種異常,可在try/except語句中再添加一個(gè)except子句。
try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y) except ZeroDivisionError: print("The second number can't be zero!") except TypeError:print("That wasn't a number,was it?")一箭雙雕
如果要使用一個(gè)except子句捕獲多種異常,可在一個(gè)元組中指定這些異常。
在except子句中,異常兩邊的圓括號(hào)很重要。
在上述代碼中,如果用戶輸入字符串、其他非數(shù)字值或輸入的第二個(gè)數(shù)為零,都將打印同樣的錯(cuò)誤消息。當(dāng)然,僅僅打印錯(cuò)誤消息幫助不大。
捕獲對(duì)象
try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y) except (ZeroDivisionError,TypeError) as e: print(e)這里的except子句捕獲兩種異常,但由于同時(shí)顯式地捕獲了對(duì)象本身,因此可將其打印出來,讓用戶知道發(fā)生了什么情況。
一網(wǎng)打盡
即使程序處理了好幾種異常,還是可能有一些漏網(wǎng)之魚。
如果用戶在提示時(shí)不輸入任何內(nèi)容就按回車鍵,將出現(xiàn)一條錯(cuò)誤消息,這種異常未被try/except語句捕獲,這理所當(dāng)然,因?yàn)槟銢]有預(yù)測(cè)到這種問題,也沒有采取相應(yīng)的措施。
像這樣捕獲所有的異常很危險(xiǎn),因?yàn)檫@不僅會(huì)隱藏你有心理準(zhǔn)備的錯(cuò)誤,還會(huì)隱藏你沒有考慮過的錯(cuò)誤。
在大多數(shù)情況下,更好的選擇是使用except Exception as e并對(duì)異常對(duì)象進(jìn)行檢查。
SystemExit和KeyboardInterrupt從**BaseException(Exception的超類)**派生而來
萬事大吉
使用except Exception as e的小技巧在這個(gè)小型除法程序中打印更有用的錯(cuò)誤消息。
while True:try:x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) value = x / yprint("x / y is",value)except Exception as e:print("Invalid input:",e)print('Please try again')else:break''' Enter the first number: 1014 Enter the second number: beyond Invalid input: invalid literal for int() with base 10: 'beyond' Please try again Enter the first number: 1202 Enter the second number: Invalid input: invalid literal for int() with base 10: '' Please try again Enter the first number: 1014 Enter the second number: 0522 x / y is 1.9425287356321839 '''最后
最后,還有finally子句,可用于在發(fā)生異常時(shí)執(zhí)行清理工作。這個(gè)子句是與try子句配套的。
finally子句非常適合用于確保文件或網(wǎng)絡(luò)套接字等得以關(guān)閉。
不管try子句中發(fā)生什么異常,都將執(zhí)行finally子句。
異常和函數(shù)
異常和函數(shù)有著天然的聯(lián)系。
如果不處理函數(shù)中引發(fā)的異常,它將向上傳播到調(diào)用函數(shù)的地方。
如果在那里也未得到處理,異常將繼續(xù)傳播,直至到達(dá)主程序(全局作用域)。
如果主程序中也沒有異常處理程序,程序?qū)⒔K止并顯示棧跟蹤消息。
faulty中引發(fā)的異常依次從faulty和ignore_exception向外傳播,最終導(dǎo)致顯示一條棧跟蹤消息。
調(diào)用handle_exception時(shí),異常最終傳播到handle_exception,并被這里的try/except語句處理。
異常之禪
假設(shè)有一個(gè)字典,你要在指定的鍵存在時(shí)打印與之相關(guān)聯(lián)的值,否則什么都不做。
def describe_person(person):print('Description of',person['name'])print('Age:',person['age'])if 'occupation' in person:print('Occupation:',person['occupation'])上面的這段代碼很直觀,但效率不高(雖然這里的重點(diǎn)是代碼簡(jiǎn)潔),
因?yàn)樗仨殐纱尾檎襬ccupation’鍵:
一次檢查這個(gè)鍵是否存在(在條件中),
另一次獲取這個(gè)鍵關(guān)聯(lián)的值,以便將其打印出來。
上面的這個(gè)函數(shù)直接假設(shè)存在’occupation’鍵。如果這種假設(shè)正確,就能省點(diǎn)事:直接獲取并打印值,而無需檢查這個(gè)鍵是否存在。如果這個(gè)鍵不存在,將引發(fā)KeyError異常,而except子句將捕獲這個(gè)異常。
檢查一個(gè)對(duì)象是否包含屬性write
try:obj.write except AttributeError:print('The object is not writeable') else:print('The object is writeable')try子句只是訪問屬性write,而沒有使用它來做任何事情。
如果引發(fā)了AttributeError異常,說明對(duì)象沒有屬性write,否則就說明有這個(gè)屬性。
在很多情況下,相比于使用if/else,使用try/except語句更自然,也更符合Python的風(fēng)格。
不那么異常的情況
如果你只想發(fā)出警告,指出情況偏離了正軌,可使用模塊warnings中的函數(shù)warn。
警告只顯示一次。如果再次運(yùn)行最后一行代碼,什么事情都不會(huì)發(fā)生。
如果其他代碼在使用你的模塊,可使用模塊warnings中的函數(shù)filterwarnings來抑制你發(fā)出的警告(或特定類型的警告),并指定要采取的措施,如"error"或"ignore"。
from warnings import filterwarnings filterwarnings("ignore") warn("Anyone out there?") filterwarnings("error") warn("Something is very wrong!")''' UserWarning Traceback (most recent call last) <ipython-input-13-2678cd9c1908> in <module>()3 warn("Anyone out there?")4 filterwarnings("error") ----> 5 warn("Something is very wrong!")UserWarning: Something is very wrong! '''上述引發(fā)的異常為UserWarning。發(fā)出警告時(shí),可指定將引發(fā)的異常(即警告類別),但必須是Warning的子類。如果將警告轉(zhuǎn)換為錯(cuò)誤,將使用你指定的異常。另外,還可根據(jù)異常來過濾掉特定類型的警告。
from warnings import filterwarnings filterwarnings("error") warn("This function is really old...",DeprecationWarning)''' DeprecationWarning Traceback (most recent call last) <ipython-input-14-db2d386b9ad9> in <module>()1 from warnings import filterwarnings2 filterwarnings("error") ----> 3 warn("This function is really old...",DeprecationWarning)DeprecationWarning: This function is really old... ''' from warnings import filterwarnings filterwarnings("ignore", category=DeprecationWarning) warn("Another deprecation warning.", DeprecationWarning) warn("Something else.")''' UserWarning Traceback (most recent call last) <ipython-input-15-2ae8758ff90f> in <module>()1 filterwarnings("ignore", category=DeprecationWarning)2 warn("Another deprecation warning.", DeprecationWarning) ----> 3 warn("Something else.")UserWarning: Something else. '''小結(jié)
| 異常對(duì)象 | 異常情況(如發(fā)生錯(cuò)誤)是用異常對(duì)象表示的。對(duì)于異常情況,有多種處理方式;如果忽略,將導(dǎo)致程序終止。 |
| 引發(fā)異常 | 可使用raise語句來引發(fā)異常。它將一個(gè)異常類或異常實(shí)例作為參數(shù),但你也可提供兩個(gè)參數(shù)(異常和錯(cuò)誤消息)。如果在except子句中調(diào)用raise時(shí)沒有提供任何參數(shù),它將重新引發(fā)該子句捕獲的異常。 |
| 自定義的異常類 | 你可通過從Exception派生來創(chuàng)建自定義的異常。 |
| 捕獲異常 | 要捕獲異常,可在try語句中使用except子句。在except子句中,如果沒有指定異常類,將捕獲所有的異常。你可指定多個(gè)異常類,方法是將它們放在元組中。如果向except提供兩個(gè)參數(shù),第二個(gè)參數(shù)將關(guān)聯(lián)到異常對(duì)象。在同一條try/except語句中,可包含多個(gè)except子句,以便對(duì)不同的異常采取不同的措施。 |
| else子句 | 除except子句外,你還可使用else子句,它在主try塊沒有引發(fā)異常時(shí)執(zhí)行。 |
| finally | 要確保代碼塊(如清理代碼)無論是否引發(fā)異常都將執(zhí)行,可使用try/finally,并將代碼塊放在finally子句中。 |
| 異常和函數(shù) | 在函數(shù)中引發(fā)異常時(shí),異常將傳播到調(diào)用函數(shù)的地方(對(duì)方法來說亦如此)。 |
| 警告 | 警告類似于異常,但(通常)只打印一條錯(cuò)誤消息。你可指定警告類別,它們是Warning的子類。 |
本章介紹的新函數(shù)
| warnings.filterwarnings(action,category=Warning, …) | 用于過濾警告 |
| warnings.warn(message, category=None) | 用于發(fā)出警告 |
總結(jié)
- 上一篇: 江湖悠悠招引台怎么建造
- 下一篇: 当贝市场里有没有百度app