1異常
1.1異常簡(jiǎn)介
異常就是程序運(yùn)行錯(cuò)誤或者邏輯混亂。不能讓用戶看到直接代碼的報(bào)錯(cuò),需要給客戶友好的提示。
異常一般是程序中無(wú)法完全避免,一般通過(guò)一定的手段,盡量減少異常的發(fā)生。提高代碼的健壯性。
看如下示例:
print('-----test--1---')
open('123.txt','r')
print('-----test--2---')
運(yùn)行結(jié)果:
說(shuō)明:
打開(kāi)一個(gè)不存在的文件123.txt,當(dāng)找不到123.txt文件時(shí),就會(huì)拋出給我們一個(gè)IOError類型的錯(cuò)誤,No?such?file?or?directory:123.txt(沒(méi)有123.txt這樣的文件或目錄)
異常:
當(dāng)Python檢測(cè)到一個(gè)錯(cuò)誤時(shí),解釋器就無(wú)法繼續(xù)執(zhí)行了,反而出現(xiàn)了一些錯(cuò)誤的提示,這就是所謂的"異常"
1.2捕獲異常
1.2.1捕獲異常try...except...
看如下示例:
try:
print('-----test--1---')
open('123.txt','r')
print('-----test--2---')
exceptIOError:
pass
說(shuō)明:
·此程序看不到任何錯(cuò)誤,因?yàn)橛胑xcept捕獲到了IOError異常,并添加了處理的方法
·pass表示實(shí)現(xiàn)了相應(yīng)的實(shí)現(xiàn),但什么也不做;如果把pass改為print語(yǔ)句,那么就會(huì)輸出其他信息
小總結(jié):

·把可能出現(xiàn)問(wèn)題的代碼,放在try中
·把處理異常的代碼,放在except中
1.1.1except捕獲多個(gè)異常
看如下示例:
try:
printnum
exceptIOError:
print('產(chǎn)生錯(cuò)誤了')
運(yùn)行結(jié)果如下:
想一想:
上例程序,已經(jīng)使用except來(lái)捕獲異常了,為什么還會(huì)看到錯(cuò)誤的信息提示?
答:
except捕獲的錯(cuò)誤類型是IOError,而此時(shí)程序產(chǎn)生的異常為NameError,所以except沒(méi)有生效
修改后的代碼為:
try:
printnum
exceptNameError:
print('產(chǎn)生錯(cuò)誤了')
運(yùn)行結(jié)果如下:
實(shí)際開(kāi)發(fā)中,捕獲多個(gè)異常的方式,如下:
#coding=utf-8
try:
print('-----test--1---')
open('123.txt','r')#如果123.txt文件不存在,那么會(huì)產(chǎn)生IOError異常
print('-----test--2---')
print(num)#如果num變量沒(méi)有定義,那么會(huì)產(chǎn)生NameError異常
except(IOError,NameError):
#如果想通過(guò)一次except捕獲到多個(gè)異??梢杂靡粋€(gè)元組的方式
#?errorMsg里會(huì)保存捕獲到的錯(cuò)誤信息
print(errorMsg)
注意:
·當(dāng)捕獲多個(gè)異常時(shí),可以把要捕獲的異常的名字,放到except后,并使用元組的方式僅進(jìn)行存儲(chǔ)
1.1.2獲取異常的信息描述


1.1.1捕獲所有異常


1.1.1else
咱們應(yīng)該對(duì)else并不陌生,在if中,它的作用是當(dāng)條件不滿足時(shí)執(zhí)行的實(shí)行;同樣在try...except...中也是如此,即如果沒(méi)有捕獲到異常,那么就執(zhí)行else中的事情
try:
#num?=?100
print(num)
except?NameError?as?errorMsg:
print('產(chǎn)生錯(cuò)誤了:%s'%errorMsg)
else:
print('沒(méi)有捕獲到異常,真高興')
print('over...')

try:
num?=?100
print?num
except?Exception?as?ex:
print('產(chǎn)生錯(cuò)誤了:%s'%ex)
else:
print('沒(méi)有捕獲到異常,真高興')
print('over...')
1.1.1try...finally...
try...finally...語(yǔ)句用來(lái)表達(dá)這樣的情況:
在程序中,如果一個(gè)段代碼必須要執(zhí)行,即無(wú)論異常是否產(chǎn)生都要執(zhí)行,那么此時(shí)就需要使用finally。?比如文件關(guān)閉,釋放鎖,把數(shù)據(jù)庫(kù)連接返還給連接池等
demo:
importtime
try:
f?=?open('test.txt')
try:
whileTrue:
content?=?f.readline()
iflen(content)?==0:
break
time.sleep(2)
print(content)
except:
#如果在讀取文件的過(guò)程中,產(chǎn)生了異常,那么就會(huì)捕獲到
#比如?按下了ctrl+c
pass
finally:
f.close()
print('關(guān)閉文件')
except:
print("沒(méi)有這個(gè)文件")
說(shuō)明:
test.txt文件中每一行數(shù)據(jù)打印,但是我有意在每打印一行之前用time.sleep方法暫停2秒鐘。這樣做的原因是讓程序運(yùn)行得慢一些。在程序運(yùn)行的時(shí)候,按Ctrl+c中斷(取消)程序。
我們可以觀察到KeyboardInterrupt異常被觸發(fā),程序退出。但是在程序退出之前,finally從句仍然被執(zhí)行,把文件關(guān)閉。
1.1異常的傳遞
1.1.1try嵌套中
importtime
try:
f?=?open('test.txt')
try:
whileTrue:
content?=?f.readline()
iflen(content)?==0:
break
time.sleep(2)
print(content)
finally:
f.close()
print('關(guān)閉文件')
except:
print("沒(méi)有這個(gè)文件")
運(yùn)行結(jié)果:
In?[26]:?import?time
...:?try:
...:?????f?=?open('test.txt')
...:?????try:
...:?????????while?True:
...:?????????????content?=?f.readline()
...:?????????????if?len(content)?==?0:
...:?????????????????break
...:?????????????time.sleep(2)
...:?????????????print(content)
...:?????finally:
...:?????????f.close()
...:?????????print('關(guān)閉文件')
...:?except:
...:?????print("沒(méi)有這個(gè)文件")
...:?finally:
...:?????print("最后的finally")
...:
xxxxxxx--->這是test.txt文件中讀取到信息
^C關(guān)閉文件
沒(méi)有這個(gè)文件
最后的finally
1.1.2函數(shù)嵌套調(diào)用中
deftest1():
print("----test1-1----")
print(num)
print("----test1-2----")
deftest2():
print("----test2-1----")
test1()
print("----test2-2----")
deftest3():
try:
print("----test3-1----")
test1()
print("----test3-2----")
exceptExceptionasresult:
print("捕獲到了異常,信息是:%s"%result)
print("----test3-2----")
test3()
print("------華麗的分割線-----")
test2()
運(yùn)行結(jié)果:

總結(jié):
·如果try嵌套,那么如果里面的try沒(méi)有捕獲到這個(gè)異常,那么外面的try會(huì)接收到這個(gè)異常,然后進(jìn)行處理,如果外邊的try依然沒(méi)有捕獲到,那么再進(jìn)行傳遞。。。
·如果一個(gè)異常是在一個(gè)函數(shù)中產(chǎn)生的,例如函數(shù)A---->函數(shù)B---->函數(shù)C,而異常是在函數(shù)C中產(chǎn)生的,那么如果函數(shù)C中沒(méi)有對(duì)這個(gè)異常進(jìn)行處理,那么這個(gè)異常會(huì)傳遞到函數(shù)B中,如果函數(shù)B有異常處理那么就會(huì)按照函數(shù)B的處理方式進(jìn)行執(zhí)行;如果函數(shù)B也沒(méi)有異常處理,那么這個(gè)異常會(huì)繼續(xù)傳遞,以此類推。。。如果所有的函數(shù)都沒(méi)有處理,那么此時(shí)就會(huì)進(jìn)行異常的默認(rèn)處理,即通常見(jiàn)到的那樣
·注意觀察上圖中,當(dāng)調(diào)用test3函數(shù)時(shí),在test1函數(shù)內(nèi)部產(chǎn)生了異常,此異常被傳遞到test3函數(shù)中完成了異常處理,而當(dāng)異常處理完后,并沒(méi)有返回到函數(shù)test1中進(jìn)行執(zhí)行,而是在函數(shù)test3中繼續(xù)執(zhí)行
1.1拋出自定義的異常
你可以用raise語(yǔ)句來(lái)引發(fā)一個(gè)異常。異常/錯(cuò)誤對(duì)象必須有一個(gè)名字,且它們應(yīng)是Error或Exception類的子類
下面是一個(gè)引發(fā)異常的例子:
classShortInputException(Exception):
'''自定義的異常類'''
def__init__(self,?length,?atleast):
#super().__init__()
self.length?=?length
self.atleast?=?atleast
defmain():
try:
s?=?input('請(qǐng)輸入-->?')
iflen(s)?<3:
#?raise引發(fā)一個(gè)你定義的異常
raiseShortInputException(len(s),3)
exceptShortInputExceptionasresult:#x這個(gè)變量被綁定到了錯(cuò)誤的實(shí)例
print('ShortInputException:輸入的長(zhǎng)度是%d,長(zhǎng)度至少應(yīng)是%d'%?(result.length,?result.atleast))
else:
print('沒(méi)有異常發(fā)生.')
main()
運(yùn)行結(jié)果如下:

注意
·以上程序中,關(guān)于代碼#super().__init__()的說(shuō)明
這一行代碼,可以調(diào)用也可以不調(diào)用,建議調(diào)用,因?yàn)開(kāi)_init__方法往往是用來(lái)對(duì)創(chuàng)建完的對(duì)象進(jìn)行初始化工作,如果在子類中重寫(xiě)了父類的__init__方法,即意味著父類中的很多初始化工作沒(méi)有做,這樣就不保證程序的穩(wěn)定了,所以在以后的開(kāi)發(fā)中,如果重寫(xiě)了父類的__init__方法,最好是先調(diào)用父類的這個(gè)方法,然后再添加自己的功能
1.1異常處理中拋出異常
classTest(object):
def__init__(self,?switch):
self.switch?=?switch#開(kāi)關(guān)
defcalc(self,?a,?b):
try:
returna/b
exceptExceptionasresult:
ifself.switch:
print("捕獲開(kāi)啟,已經(jīng)捕獲到了異常,信息如下:")
print(result)
else:
#重新拋出這個(gè)異常,此時(shí)就不會(huì)被這個(gè)異常處理給捕獲到,從而觸發(fā)默認(rèn)的異常處理
raise
a?=?Test(True)
a.calc(11,0)
print("----------------------華麗的分割線----------------")
a.switch?=False
a.calc(11,0)
運(yùn)行結(jié)果:
