在執(zhí)行程序的過程中昔字,可能會(huì)遇到多多少少的“意外情況”爆袍,比如除數(shù)為 0,文件找不到作郭,變量未聲明等陨囊。解釋器在發(fā)現(xiàn)這些異常錯(cuò)誤后,會(huì)當(dāng)機(jī)立斷終止程序的運(yùn)行夹攒,如果我們想程序繼續(xù)運(yùn)行蜘醋,提高代碼的健壯性,就需要用到異常處理咏尝。
try 和 except
Python 中使用 try
關(guān)鍵字來捕獲異常压语,使用 except
關(guān)鍵字來處理異常啸罢,沒有 catch 關(guān)鍵字。
不進(jìn)行異常處理的情況:
def devide(a,b):
return a/b
devide(1,0)
執(zhí)行 devide 函數(shù)胎食,程序直接掛掉了扰才,拋出一個(gè) ZeroDivisionError
的異常:
Traceback (most recent call last):
File "C:\Users\Charley\Desktop\py\py.py", line 4, in <module>
devide(1,0)
File "C:\Users\Charley\Desktop\py\py.py", line 2, in devide
return a/b
ZeroDivisionError: division by zero
[Finished in 0.6s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]
[dir: C:\Users\Charley\Desktop\py]
[path: C:\Python27\;C:\Python27\Scripts;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\nvm;D:\nodejs;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramData\chocolatey\bin;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\Scripts\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\;D:\Git\bin;D:\Sublime Text 3;D:\MinGW\bin\;D:\MinGW\bin\;D:\Microsoft VS Code\bin;D:\Java\jdk 8.0\bin;D:\Android\sdk;C:\Program Files\MySQL\MySQL Server 5.7\bin;]
修改代碼,對(duì) ZeroDivisionError
異常進(jìn)行捕獲處理:
def devide(a,b):
try:
return a/b
except ZeroDivisionError:
print("除數(shù)不能為零斥季!")
devide(1,0)
執(zhí)行結(jié)果如下:
除數(shù)不能為零训桶!
程序沒有掛掉,并對(duì)相應(yīng)的異常進(jìn)行了處理酣倾。
捕獲多個(gè)異常
我們還可以捕獲多個(gè)異常:
def devide(a,b):
try:
print(num)
return a/b
except ZeroDivisionError:
print("除數(shù)不能為零!")
except NameError:
print("變量不存在谤专!")
devide(1,0)
運(yùn)行結(jié)果:
變量不存在躁锡!
上面的異常處理可以捕獲多個(gè)異常情況,如果觸發(fā)了 ZeroDivisionError
異常置侍,就輸出 除數(shù)不能為零映之!
提示語,如果觸發(fā)了 NameError
異常蜡坊,就輸出 變量不存在杠输!
提示語。
在捕獲多個(gè)異常時(shí)秕衙,也可以進(jìn)行簡寫:
def devide(a,b):
try:
print(num)
return a/b
except (ZeroDivisionError,NameError):
print("發(fā)生了一些異常情況蠢甲!請(qǐng)檢查代碼")
devide(1,0)
Python2 中可以將異常直接使用逗號(hào)分隔,Python3 中必須將異常置于元組中据忘。
異常類
除了上面提到的兩種異常鹦牛,Python 中還有許許多多的異常(具體見此),我們可以查看這些異常的數(shù)據(jù)類型:
def devide(a,b):
try:
return a/b
except ZeroDivisionError:
print(type(ZeroDivisionError))
devide(1,0)
執(zhí)行結(jié)果:
<class 'type'>
這些異常都是類勇吊。所有的異常都有一個(gè)共同的父類 Exception
曼追,如果我們使用 Exception
來進(jìn)行捕獲,將捕獲到所有的異常情況汉规,使用 as
關(guān)鍵字可以查看具體的異常信息:
def devide(a,b):
try:
return a/b
except Exception as res:
print(res)
devide(1,0)
運(yùn)行結(jié)果:
division by zero
raise 關(guān)鍵字
raise
關(guān)鍵字用來主動(dòng)觸發(fā)異常:
def devide(a,b):
try:
raise NameError
except Exception:
print("發(fā)生了一點(diǎn)錯(cuò)誤礼殊!")
devide(1,1)
運(yùn)行結(jié)果:
發(fā)生了一點(diǎn)錯(cuò)誤!
上面我們手動(dòng)觸發(fā)了 NameError
異常针史,同樣可以被捕獲晶伦。
自定義異常
除了系統(tǒng)自帶的異常之外,我們還可以對(duì)異常進(jìn)行自定義悟民,前面說到所有的異常都是類坝辫,因此我們也需要自定義異常類,該類應(yīng)該以 Exception
類作為父類射亏。
class 沒事兒就像搞點(diǎn)事情(Exception):
def __init__(self,reason):
self.reason = reason
def devide(a,b):
try:
raise 沒事兒就像搞點(diǎn)事情("點(diǎn)事情是誰近忙?")
except Exception as res:
print("發(fā)生了一點(diǎn)錯(cuò)誤:%s"%res.reason)
devide(1,1)
運(yùn)行結(jié)果如下:
發(fā)生了一點(diǎn)錯(cuò)誤:點(diǎn)事情是誰竭业?
新建對(duì)象是為了記錄詳細(xì)的異常信息,當(dāng)然也可以直接拋出異常類:
class 沒事兒就像搞點(diǎn)事情(Exception):
def __init__(self,reason):
self.reason = reason
def devide(a,b):
try:
raise 沒事兒就像搞點(diǎn)事情
except Exception as res:
print("發(fā)生了一點(diǎn)錯(cuò)誤")
devide(1,1)
運(yùn)行結(jié)果如下:
發(fā)生了一點(diǎn)錯(cuò)誤
else 關(guān)鍵字
如果沒有出現(xiàn)異常及舍,那么就會(huì)執(zhí)行 else
中的代碼:
def devide(a,b):
try:
print(a/b)
except Exception as res:
print("發(fā)生了一點(diǎn)錯(cuò)誤:%s"%res)
else:
print("嘻嘻未辆,沒有發(fā)生錯(cuò)誤喲!")
devide(1,1)
運(yùn)行結(jié)果如下:
1.0
嘻嘻锯玛,沒有發(fā)生錯(cuò)誤喲咐柜!
finally 關(guān)鍵字
finally
是異常的出口,不管有沒有異常攘残,不管捕獲了多少異常拙友,都會(huì)執(zhí)行 finally
中的語句:
def devide(a,b):
try:
print(a/b)
except Exception as res:
print("發(fā)生了一點(diǎn)錯(cuò)誤:%s"%res)
else:
print("嘻嘻,沒有發(fā)生錯(cuò)誤喲歼郭!")
finally:
print("你若安好遗契,便是晴天")
devide(1,1)
運(yùn)行結(jié)果:
1.0
嘻嘻,沒有發(fā)生錯(cuò)誤喲病曾!
你若安好牍蜂,便是晴天
finally
中可以進(jìn)行清理工作,比如關(guān)閉文件泰涂,該操作不論是否發(fā)生異常都應(yīng)該被執(zhí)行了鲫竞,所以應(yīng)該放在 finally
中。
異常傳遞
異常會(huì)在調(diào)用棧中逐層往外傳遞逼蒙,直到被捕獲到為止从绘。內(nèi)層函數(shù)產(chǎn)生了異常,如果自身沒有進(jìn)行處理其做,就將異常傳遞給調(diào)用它的函數(shù)顶考,如果調(diào)用它的函數(shù)也不進(jìn)行處理,再向外傳遞妖泄,直到傳遞給解釋器驹沿,被解釋器所捕獲。
def a():
print(num)
def b():
a()
def c():
try:
b()
except Exception as res:
print(res)
c()
運(yùn)行結(jié)果如下:
name 'num' is not defined
完蹈胡。