一文讀懂Python中的異常處理

異常處理在任何一門編程語言里都是值得關(guān)注的一個(gè)話題爸业,良好的異常處理可以讓你的程序更加健壯矫渔,清晰的錯(cuò)誤信息更能幫助你快速修復(fù)問題崔列。在Python中,和不部分高級(jí)語言一樣屁桑,使用了try/except/finally語句塊來處理異常医寿,如果你有其他編程語言的經(jīng)驗(yàn),實(shí)踐起來并不難蘑斧。

異常處理語句 try...excpet...finally

實(shí)例代碼

defdiv(a, b):

try:

print(a / b)

exceptZeroDivisionError:

print("Error: b should not be 0 !!")

exceptExceptionase:

print("Unexpected Error: {}".format(e))

else:

print('Run into else only when everything goes well')

finally:

print('Always run into finally block.')

# tests

div(2,0)

div(2,'bad type')

div(1,2)

# Mutiple exception in one line

try:

print(a / b)

except(ZeroDivisionError, TypeError)ase:

print(e)

# Except block is optional when there is finally

try:

open(database)

finally:

close(database)

# catch all errors and log it

try:

do_work()

except:

# get detail from logging module

logging.exception('Exception caught!')

# get detail from sys.exc_info() method

error_type, error_value, trace_back = sys.exc_info()

print(error_value)

raise

總結(jié)如下

except語句不是必須的靖秩,finally語句也不是必須的,但是二者必須要有一個(gè)竖瘾,否則就沒有try的意義了沟突。

except語句可以有多個(gè),Python會(huì)按except語句的順序依次匹配你指定的異常准浴,如果異常已經(jīng)處理就不會(huì)再進(jìn)入后面的except語句事扭。

except語句可以以元組形式同時(shí)指定多個(gè)異常,參見實(shí)例代碼乐横。

except語句后面如果不指定異常類型,則默認(rèn)捕獲所有異常今野,你可以通過logging或者sys模塊獲取當(dāng)前異常葡公。

如果要捕獲異常后要重復(fù)拋出,請使用raise条霜,后面不要帶任何參數(shù)或信息催什。

不建議捕獲并拋出同一個(gè)異常,請考慮重構(gòu)你的代碼宰睡。

不建議在不清楚邏輯的情況下捕獲所有異常蒲凶,有可能你隱藏了很嚴(yán)重的問題。

盡量使用內(nèi)置的異常處理語句來 替換try/except語句拆内,比如with語句旋圆,getattr()方法。

拋出異常 raise

如果你需要自主拋出異常一個(gè)異常麸恍,可以使用raise關(guān)鍵字灵巧,等同于C#和Java中的throw語句,其語法規(guī)則如下抹沪。

raiseNameError("bad name!")

raise關(guān)鍵字后面需要指定你拋出的異常類型刻肄,一般來說拋出的異常越詳細(xì)越好,Python在exceptions模塊內(nèi)建了很多的異常類型融欧,通過使用dir()函數(shù)來查看exceptions中的異常類型敏弃,如下:

importexceptions

# ['ArithmeticError', 'AssertionError'.....]

printdir(exceptions)

當(dāng)然你也可以查閱Python的文檔庫進(jìn)行更詳細(xì)的了解。

https://docs.python.org/2.7/library/exceptions.html#bltin-exceptions

自定義異常類型

Python中也可以自定義自己的特殊類型的異常噪馏,只需要要從Exception類繼承(直接或間接)即可:

classSomeCustomException(Exception):

pass

一般你在自定義異常類型時(shí)麦到,需要考慮的問題應(yīng)該是這個(gè)異常所應(yīng)用的場景虹茶。如果內(nèi)置異常已經(jīng)包括了你需要的異常,建議考慮使用內(nèi)置 的異常類型隅要。比如你希望在函數(shù)參數(shù)錯(cuò)誤時(shí)拋出一個(gè)異常蝴罪,你可能并不需要定義一個(gè)InvalidArgumentError,使用內(nèi)置的ValueError即可步清。

經(jīng)驗(yàn)案例

傳遞異常 re-raise Exception

捕捉到了異常要门,但是又想重新引發(fā)它(傳遞異常),使用不帶參數(shù)的raise語句即可:

deff1():

print(1/0)

deff2():

try:

f1()

exceptExceptionase:

raise# don't raise e !!!

f2()

在Python2中廓啊,為了保持異常的完整信息欢搜,那么你捕獲后再次拋出時(shí)千萬不能在raise后面加上異常對(duì)象,否則你的trace信息就會(huì)從此處截?cái)?/b>谴轮。以上是最簡單的重新拋出異常的做法炒瘟。

還有一些技巧可以考慮,比如拋出異常前對(duì)異常的信息進(jìn)行更新第步。

deff2():

try:

f1()

exceptExceptionase:

e.args += ('more info',)

raise

如果你有興趣了解更多疮装,建議閱讀這篇博客。

http://www.ianbicking.org/blog/2007/09/re-raising-exceptions.html

Python3對(duì)重復(fù)傳遞異常有所改進(jìn)粘都,你可以自己嘗試一下廓推,不過建議還是同上。

Exception 和 BaseException

當(dāng)我們要捕獲一個(gè)通用異常時(shí)翩隧,應(yīng)該用Exception還是BaseException樊展?我建議你還是看一下?官方文檔說明,這兩個(gè)異常到底有啥區(qū)別呢堆生? 請看它們之間的繼承關(guān)系专缠。

BaseException

+-- SystemExit

+-- KeyboardInterrupt

+-- GeneratorExit

+-- Exception

? ? ?+-- StopIteration...

? ? ?+-- StandardError...

? ? ?+-- Warning...

從Exception的層級(jí)結(jié)構(gòu)來看,BaseException是最基礎(chǔ)的異常類淑仆,Exception繼承了它涝婉。BaseException除了包含所有的Exception外還包含了SystemExit,KeyboardInterrupt和GeneratorExit三個(gè)異常糯景。

有此看來你的程序在捕獲所有異常時(shí)更應(yīng)該使用Exception而不是BaseException嘁圈,因?yàn)榱硗馊齻€(gè)異常屬于更高級(jí)別的異常,合理的做法應(yīng)該是交給Python的解釋器處理蟀淮。

except Exception as e和 except Exception, e

代碼示例如下:

try:

do_something()

exceptNameErrorase:# should

pass

exceptKeyError, e:# should not

pass

在Python2的時(shí)代最住,你可以使用以上兩種寫法中的任意一種。在Python3中你只能使用第一種寫法怠惶,第二種寫法被廢棄掉了涨缚。第一個(gè)種寫法可讀性更好,而且為了程序的兼容性和后期移植的成本,請你也拋棄第二種寫法脓魏。

raise "Exception string"

把字符串當(dāng)成異常拋出看上去是一個(gè)非常簡潔的辦法兰吟,但其實(shí)是一個(gè)非常不好的習(xí)慣。

ifis_work_done():

pass

else:

raise"Work is not done!"# not cool

上面的語句如果拋出異常茂翔,那么會(huì)是這樣的:

Traceback (most recent call last):

File"/demo/exception_hanlding.py", line48,in

raise"Work is not done!"

TypeError: exceptions must be old-style classesorderivedfromBaseException,notstr

這在Python2.4以前是可以接受的做法混蔼,但是沒有指定異常類型有可能會(huì)讓下游沒辦法正確捕獲并處理這個(gè)異常,從而導(dǎo)致你的程序掛掉珊燎。簡單說惭嚣,這種寫法是是封建時(shí)代的陋習(xí),應(yīng)該扔了悔政。

使用內(nèi)置的語法范式代替try/except

Python 本身提供了很多的語法范式簡化了異常的處理晚吞,比如for語句就處理的StopIteration異常,讓你很流暢地寫出一個(gè)循環(huán)谋国。

with語句在打開文件后會(huì)自動(dòng)調(diào)用finally中的關(guān)閉文件操作槽地。我們在寫Python代碼時(shí)應(yīng)該盡量避免在遇到這種情況時(shí)還使用try/except/finally的思維來處理。

# should not

try:

f = open(a_file)

do_something(f)

finally:

f.close()

# should

withopen(a_file)asf:

do_something(f)

再比如芦瘾,當(dāng)我們需要訪問一個(gè)不確定的屬性時(shí)捌蚊,有可能你會(huì)寫出這樣的代碼:

try:

test = Test()

name = test.name# not sure if we can get its name

exceptAttributeError:

name ='default'

其實(shí)你可以使用更簡單的getattr()來達(dá)到你的目的。

name = getattr(test,'name','default')

最佳實(shí)踐

最佳實(shí)踐不限于編程語言旅急,只是一些規(guī)則和填坑后的收獲逢勾。

只處理你知道的異常,避免捕獲所有 異常然后吞掉它們藐吮。

拋出的異常應(yīng)該說明原因,有時(shí)候你知道異常類型也猜不出所以然的逃贝。

避免在catch語句塊中干一些沒意義的事情谣辞。

不要使用異常來控制流程,那樣你的程序會(huì)無比難懂和難維護(hù)沐扳。

如果有需要泥从,切記使用finally來釋放資源。

如果有需要沪摄,請不要忘記在處理異常后做清理工作或者回滾操作躯嫉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市杨拐,隨后出現(xiàn)的幾起案子祈餐,更是在濱河造成了極大的恐慌,老刑警劉巖哄陶,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帆阳,死亡現(xiàn)場離奇詭異,居然都是意外死亡屋吨,警方通過查閱死者的電腦和手機(jī)蜒谤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門山宾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳍徽,你說我怎么就攤上這事资锰。” “怎么了阶祭?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵绷杜,是天一觀的道長。 經(jīng)常有香客問我胖翰,道長接剩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任萨咳,我火速辦了婚禮懊缺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘培他。我一直安慰自己鹃两,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布舀凛。 她就那樣靜靜地躺著俊扳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猛遍。 梳的紋絲不亂的頭發(fā)上馋记,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音懊烤,去河邊找鬼梯醒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛腌紧,可吹牛的內(nèi)容都是我干的茸习。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壁肋,長吁一口氣:“原來是場噩夢啊……” “哼号胚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浸遗,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤猫胁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后乙帮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杜漠,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驾茴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盼樟。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锈至,靈堂內(nèi)的尸體忽然破棺而出晨缴,到底是詐尸還是另有隱情,我是刑警寧澤峡捡,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布击碗,位于F島的核電站,受9級(jí)特大地震影響们拙,放射性物質(zhì)發(fā)生泄漏稍途。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一砚婆、第九天 我趴在偏房一處隱蔽的房頂上張望械拍。 院中可真熱鬧,春花似錦装盯、人聲如沸坷虑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迄损。三九已至,卻和暖如春账磺,著一層夾襖步出監(jiān)牢的瞬間芹敌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工垮抗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留党窜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓借宵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矾削。 傳聞我的和親對(duì)象是個(gè)殘疾皇子壤玫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一、簡介 Python最強(qiáng)大的結(jié)構(gòu)之一就是它的異常處理能力哼凯,所有的標(biāo)準(zhǔn)異常都使用類來實(shí)現(xiàn)欲间,都是基類Exceptio...
    隨風(fēng)化作雨閱讀 3,073評(píng)論 0 1
  • 1.什么是異常(what) 異常:不正常的情況 不正常的情況,在程序中断部,會(huì)有兩種體現(xiàn): l自己造孽:寫錯(cuò)代碼了猎贴!錯(cuò)...
    Customer_閱讀 706評(píng)論 0 0
  • 像是在尋找世界,其實(shí),我們在尋找自己她渴。 我熱愛在安靜的世界里达址,尋找自由的快樂與靜謐,緩沖并享受這個(gè)溫暖時(shí)光趁耗;我熱愛...
    就帶著夢想陽光去旅行閱讀 115評(píng)論 0 0
  • 文/藍(lán)天 一月十二日 雨 無雪的冬沉唠,醞釀了幾日的雨,終于在這個(gè)陰沉凄冷的午后悄然而落苛败,淅淅瀝瀝的如三月的春雨满葛,飄...
    西方家的閱讀 339評(píng)論 2 2
  • 又看了一遍哈佛女孩許吉如的演講《國強(qiáng)則少年強(qiáng)》。 感觸很深罢屈,仿佛太多時(shí)候嘀韧,我們覺得,現(xiàn)在的安逸都是理所當(dāng)然的缠捌,就像...
    顧小孤的N次方閱讀 1,099評(píng)論 5 6