使用斷言表達(dá)式,通常會(huì)有人誤用它,所以我決定寫一篇文章來說明何時(shí)使用斷言,什么時(shí)候不用。為那些還不清楚它的人,Python的assert是用來檢查一個(gè)條件,如果它為真,就不做任何事。如果它為假,則會(huì)拋出AssertError并且包含錯(cuò)誤信息。例如:pyx=23pyassertx0,"xisnotzeroornegative"pyassertx%2==0,"xisnotanevennumber"Traceback(mostrecentcalllast):File"",line1,inAssertionError:xisnotanevennumber很多人用assert作為一個(gè)很快和容易的方法來在參數(shù)錯(cuò)誤的時(shí)候拋出異常。但這樣做是錯(cuò)的,非常錯(cuò)誤,有兩個(gè)原因。首先AssertError不是在測試參數(shù)時(shí)應(yīng)該拋出的錯(cuò)誤。你不應(yīng)該像這樣寫代碼:ifnotisinstance(x,int):raiseAssertionError("notanint")你應(yīng)該拋出TypeError的錯(cuò)誤,assert會(huì)拋出錯(cuò)誤的異常。但是,更危險(xiǎn)的是,有一個(gè)關(guān)于assert的困擾:它可以被編譯好然后從來不執(zhí)行,如果你用–O或–oo選項(xiàng)運(yùn)行Python,結(jié)果不保證assert表達(dá)式會(huì)運(yùn)行到。當(dāng)適當(dāng)?shù)氖褂胊ssert時(shí),這是未來,但是當(dāng)assert不恰當(dāng)?shù)氖褂脮r(shí),它會(huì)讓代碼用-O執(zhí)行時(shí)出錯(cuò)。那什么時(shí)候應(yīng)該使用assert?沒有特定的規(guī)則,斷言應(yīng)該用于:防御型的編程運(yùn)行時(shí)檢查程序邏輯檢查約定程序常量檢查文檔(在測試代碼的時(shí)候使用斷言也是可接受的,是一種很方便的單元測試方法,你接受這些測試在用-O標(biāo)志運(yùn)行時(shí)不會(huì)做任何事。我有時(shí)在代碼里使用assertFalse來標(biāo)記沒有寫完的代碼分支,我希望這些代碼運(yùn)行失敗。盡管拋出NotImplementedError可能會(huì)更好。)關(guān)于斷言的意見有很多,因?yàn)樗艽_保代碼的正確性。如果你確定代碼是正確的,那么就沒有用斷言的必要了,因?yàn)樗麄儚膩聿粫?huì)運(yùn)行失敗,你可以直接移除這些斷言。如果你確定檢查會(huì)失敗,那么如果你不用斷言,代碼就會(huì)通過編譯并忽略你的檢查。在以上兩種情況下會(huì)很有意思,當(dāng)你比較肯定代碼但是不是絕對(duì)肯定時(shí)??赡苣銜?huì)錯(cuò)過一些非常古怪的情況。在這個(gè)情況下,額外的運(yùn)行時(shí)檢查能幫你確保任何錯(cuò)誤都會(huì)盡早地被捕捉到。另一個(gè)好的使用斷言的方式是檢查程序的不變量。一個(gè)不變量是一些你需要依賴它為真的情況,除非一個(gè)bug導(dǎo)致它為假。如果有bug,最好能夠盡早發(fā)現(xiàn),所以我們?yōu)樗M(jìn)行一個(gè)測試,但是又不想減慢代碼運(yùn)行速度。所以就用斷言,因?yàn)樗茉陂_發(fā)時(shí)打開,在產(chǎn)品階段關(guān)閉。一個(gè)非變量的例子可能是,如果你的函數(shù)希望在它開始時(shí)有數(shù)據(jù)庫的連接,并且承諾在它返回的時(shí)候仍然保持連接,這就是函數(shù)的不變量:defsome_function(arg):assertnotDB.closed()#codegoeshereassertnotDB.closed()returnresult斷言本身就是很好的注釋,勝過你直接寫注釋:#whenwereachhere,weknowthatn2你可以通過添加斷言來確保它:assertn2斷言也是一種防御型編程。你不是讓你的代碼防御現(xiàn)在的錯(cuò)誤,而是防止在代碼修改后引發(fā)的錯(cuò)誤。理想情況下,單元測試可以完成這樣的工作,可是需要面對(duì)的現(xiàn)實(shí)是,它們通常是沒有完成的。人們可能在提交代碼前會(huì)忘了運(yùn)行測試代碼。有一個(gè)內(nèi)部檢查是另一個(gè)阻擋錯(cuò)誤的防線,尤其是那些不明顯的錯(cuò)誤,卻導(dǎo)致了代碼出問題并且返回錯(cuò)誤的結(jié)果。加入你有一些if…elif的語句塊,你知道在這之前一些需要有一些值:#targetisexpectedtobeoneofx,y,orz,andnothingelse.iftarget==x:run_x_code()eliftarget==y:run_y_code()else:run_z_code()假設(shè)代碼現(xiàn)在是完全正確的。但它會(huì)一直是正確的嗎?依賴的修改,代碼的修改。如果依賴修改成target=w會(huì)發(fā)生什么,會(huì)關(guān)系到run_w_code函數(shù)嗎?如果我們改變了代碼,但沒有修改這里的代碼,可能會(huì)導(dǎo)致錯(cuò)誤的調(diào)用run_z_code函數(shù)并引發(fā)錯(cuò)誤。用防御型的方法來寫代碼會(huì)很好,它能讓代碼運(yùn)行正確,或者立馬執(zhí)行錯(cuò)誤,即使你在未來對(duì)它進(jìn)行了修改。在代碼開頭的注釋很好的一步,但是人們經(jīng)常懶得讀或者更新注釋。一旦發(fā)生這種情況,注釋會(huì)變得沒用。但有了斷言,我可以同時(shí)對(duì)代碼塊的假設(shè)書寫文檔,并且在它們違反的時(shí)候觸發(fā)一個(gè)干凈的錯(cuò)誤asserttargetin(x,y,z)iftarget==x:run_x_code()eliftarget==y:run_y_code()else:asserttarget==zrun_z_code()這樣,斷言是一種防御型編程,同時(shí)也是一種文檔。我想到一個(gè)更好的方案:iftarget==x:run_x_code()eliftarget==y:run_y_code()eliftarget==z:run_z_code()else:#Thiscanneverhappen.ButjustincaseitdoesraiseRuntimeError("anunexpectederroroccurred")按約定進(jìn)行設(shè)計(jì)是斷言的另一個(gè)好的用途。我們想象函數(shù)與調(diào)用者之間有個(gè)約定,比如下面的:“如果你傳給我一個(gè)非空字符串,我保證傳會(huì)字符串的第一個(gè)字母并將其大寫。”如果約定被函數(shù)或調(diào)用這破壞,代碼就會(huì)出問題。我們說函數(shù)有一些前置條件和后置條件,所以函數(shù)就會(huì)這么寫:deffirst_upper(astring):assertisinstance(astring,str)andlen(astring)0result=astring[0].upper()assertisinstance(result,str)andlen(result)==1assertresult==result.upper()returnresult按約定設(shè)計(jì)的目標(biāo)是為了正確的編程,前置條件和后置條件是需要保持的。這是斷言的典型應(yīng)用場景,因?yàn)橐坏┪覀儼l(fā)布了沒有問題的代碼到產(chǎn)品中,程序會(huì)是正確的,并且我們能安全的移除檢查。下面是我建議的不要用斷言的場景:不要用它測試用戶提供的數(shù)據(jù)不要用斷言來檢查你覺得在你的程序的常規(guī)使用時(shí)會(huì)出錯(cuò)的地方。斷言是用來檢查非常罕見的問題。你的用戶不應(yīng)該看到任何斷言錯(cuò)誤,如果他們看到了,這是一個(gè)bug,修復(fù)它。有的情況下,不用斷言是因?yàn)樗染_的檢查要短,它不應(yīng)該是懶碼農(nóng)的偷懶方式。不要用它來檢查對(duì)公共庫的輸入?yún)?shù),因?yàn)樗荒芸刂普{(diào)用者,所以不能保證調(diào)用者會(huì)不會(huì)打破雙方的約定。不要為你覺得可以恢復(fù)的錯(cuò)誤用斷言。換句話說,不用改在產(chǎn)品代碼里捕捉到斷言錯(cuò)誤。不要用太多斷言以至于讓代碼很晦澀。
創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷、網(wǎng)站重做改版、柴桑網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、成都做商城網(wǎng)站、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)公司、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為柴桑等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
assertIsNotNone(testValue, message)
定義: 單元測試庫函數(shù),用于單元測試中以檢查輸入值是否為None。
輸入值:
testValue:變量,需要測試的變量。
message:字符串,測試消息失敗時(shí)顯示的消息。
返回值: 根據(jù)斷言條件返回布爾值,如果輸入值滿足assertIsNotNone()將返回true,否則返回false。
函數(shù)名:
1. 判斷是否相等
assertEqual
assertNotEqual
2. 判斷真假
assertTrue
assertFalse
3. 判斷是否為空
assertIsNone
assertIsNotNone
返回值: 滿足斷言,返回True;不滿足斷言,返回False。
assert是斷言語句,判斷其后的表達(dá)式是否為真,如果值為False則退出程序,是用來調(diào)試程序的語句
return 用于從函數(shù)中返回(值)
兩者沒有聯(lián)系
根據(jù)老外的解釋就是說assertEqual與assertEquals沒有區(qū)別,可以說是完全一樣的函數(shù),而現(xiàn)在assertEquals函數(shù)已經(jīng)被棄用,也就說不建議你使用了,以后可能這個(gè)方法就在python中消失了,在python3.0中已經(jīng)趨向使用不帶s的assert方法了,但是現(xiàn)在仍然沒有刪掉的原因是因?yàn)橛幸恍┡f代碼和項(xiàng)目在使用帶s的方法,語言要保持舊代碼的兼容性。
至于assert那就很好解釋了,就是判斷0,1 也就是python中的真假關(guān)系
assertAlmostEquals這2個(gè)方法存在的原因與上面的相同,建議你不要使用帶s的方法了,這個(gè)方法是做一個(gè)粗略判斷,判斷的值為你4舍5入后的值,也就是說5.1與5.2是相等的,如果使用這樣的assert方法。