Day-10: 错误、调试和测试
程序運(yùn)行時(shí),會(huì)遇到各種各樣的錯(cuò)誤。
編寫(xiě)錯(cuò)誤叫做bug,而另一類(lèi)由于運(yùn)行過(guò)程中無(wú)法預(yù)測(cè)的,比如寫(xiě)文件時(shí),磁盤(pán)滿(mǎn)了,寫(xiě)不進(jìn)去;或者從網(wǎng)絡(luò)抓取數(shù)據(jù),網(wǎng)絡(luò)突然掉了。這些錯(cuò)誤稱(chēng)為異常,程序中需要對(duì)異常進(jìn)行處理,使得程序能夠運(yùn)行下去。
- 錯(cuò)誤處理
Python中,程序運(yùn)行錯(cuò)誤時(shí),如果錯(cuò)誤沒(méi)有捕獲,它會(huì)一直往上拋,最后被Python解釋器捕獲,打印一個(gè)錯(cuò)誤。
# err.py: def foo(s):return 10 / int(s)def bar(s):return foo(s) * 2def main():bar('0')main() $ python err.py Traceback (most recent call last):File "err.py", line 11, in <module>main()File "err.py", line 9, in mainbar('0')File "err.py", line 6, in barreturn foo(s) * 2File "err.py", line 3, in fooreturn 10 / int(s) ZeroDivisionError: integer division or modulo by zero從上到下,錯(cuò)誤會(huì)一層層的反饋,直到顯示最終出錯(cuò)的地方。
try...except...finally...:常用這種方法來(lái)檢查錯(cuò)誤并捕捉到,同時(shí)進(jìn)行相應(yīng)的處理。
try:print 'try...'r = 10 / 0print 'result:', r except ZeroDivisionError, e:print 'except:', e finally:print 'finally...' print 'END' try... except: integer division or modulo by zero finally... END注意到,錯(cuò)誤類(lèi)型有很多種,它們其實(shí)都是從BaseException類(lèi)派生出來(lái)的,常見(jiàn)的錯(cuò)誤類(lèi)型和繼承關(guān)系有:https://docs.python.org/2/library/exceptions.html#exception-hierarchy
- 調(diào)試
程序運(yùn)行一次就成功的概率很小,基本上不超過(guò)1%。一般有如下的調(diào)試方法:
第一種,直接在可能出錯(cuò)的地方print出來(lái),但是后期會(huì)一個(gè)個(gè)刪掉。
第二種,使用斷言來(lái)代替。
# err.py def foo(s):n = int(s)assert n != 0, 'n is zero!'return 10 / ndef main():foo('0')斷言中,如果“n != 0”是錯(cuò)的,就拋出AssertionError,并顯示后面的字符串。
第三種,使用logging。
logging有debug,info,warning,error等幾個(gè)級(jí)別,從前到后優(yōu)先級(jí)依次提高,即如果,指定level=WARNING后,debug和info就不起作用了。這樣一樣,就可以輸出不同級(jí)別的信息,也不用刪除,最后統(tǒng)一控制輸出哪個(gè)級(jí)別的信息。
logging的另一個(gè)好處是通過(guò)簡(jiǎn)單的配置,一條語(yǔ)句可以同時(shí)輸出到不同的地方,,比如console和文件。
# err.py import logginglogging.basicConfig(level=logging.INFO)s = '0' n = int(s) logging.info('n = %d' % n) print 10 / n $ python err.py INFO:root:n = 0 Traceback (most recent call last):File "err.py", line 8, in <module>print 10 / n ZeroDivisionError: integer division or modulo by zero
第四種調(diào)試方式,就是調(diào)試器pdb,讓程序以單步方式運(yùn)行,可以隨時(shí)查看運(yùn)行狀態(tài)。
# err.py s = '0' n = int(s) print 10 / n然后,以參數(shù)-m pdb啟動(dòng),單步運(yùn)行
$ python -m pdb err.py > /Users/michael/Github/sicp/err.py(2)<module>() -> s = '0' (Pdb) l1 # err.py2 -> s = '0'3 n = int(s)4 print 10 / n [EOF]輸入n單步運(yùn)行下一步
(Pdb) n > /Users/michael/Github/sicp/err.py(3)<module>() -> n = int(s) (Pdb) n > /Users/michael/Github/sicp/err.py(4)<module>() -> print 10 / n輸入p 變量名來(lái)查看變量狀態(tài)。
(Pdb) p s '0' (Pdb) p n 0輸入q結(jié)束運(yùn)行
(Pdb) n ZeroDivisionError: 'integer division or modulo by zero' > /Users/michael/Github/sicp/err.py(4)<module>() -> print 10 / n (Pdb) q另外在合適的地方,設(shè)置pdb.set_trace(),可以作為斷點(diǎn)。
# err.py import pdbs = '0' n = int(s) pdb.set_trace() # 運(yùn)行到這里會(huì)自動(dòng)暫停 print 10 / n $ python err.py > /Users/michael/Github/sicp/err.py(7)<module>() -> print 10 / n (Pdb) p n 0 (Pdb) c Traceback (most recent call last):File "err.py", line 7, in <module>print 10 / n ZeroDivisionError: integer division or modulo by zero到達(dá)斷點(diǎn)時(shí),進(jìn)入pdb調(diào)試器。
最后,還有方便的IDE調(diào)試器。
- 單元測(cè)試
單元測(cè)試,顧名思義,就是對(duì)一個(gè)部分測(cè)試,可以是一個(gè)模塊、一個(gè)函數(shù)或者一個(gè)類(lèi)。它的目的是保證該單元能夠?qū)崿F(xiàn)原先規(guī)劃的功能,為之后的整體調(diào)試做準(zhǔn)備。
例如,現(xiàn)有模塊mydict.py,對(duì)它的要求是實(shí)現(xiàn)如下功能:
>>> d = Dict(a=1, b=2) >>> d['a'] 1 >>> d.a 1mydict.py代碼如下:
class Dict(dict):def __init__(self, **kw):super(Dict, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Dict' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = value編寫(xiě)的單元測(cè)試,需要引入unittest模塊,編寫(xiě)的mydict_test.py如下:
import unittestfrom mydict import Dictclass TestDict(unittest.TestCase):def test_init(self): # 測(cè)試初始化功能d = Dict(a=1, b='test')self.assertEquals(d.a, 1)self.assertEquals(d.b, 'test')self.assertTrue(isinstance(d, dict))def test_key(self): # 測(cè)試key的功能d = Dict()d['key'] = 'value'self.assertEquals(d.key, 'value')def test_attr(self): # 測(cè)試屬性功能d = Dict()d.key = 'value'self.assertTrue('key' in d)self.assertEquals(d['key'], 'value')def test_keyerror(self): # 測(cè)試key錯(cuò)誤的功能d = Dict()with self.assertRaises(KeyError):value = d['empty']def test_attrerror(self): # 測(cè)試屬性錯(cuò)誤的功能d = Dict()with self.assertRaises(AttributeError):value = d.empty編寫(xiě)的單元測(cè)試類(lèi),從unittest.TestCase繼承。其中,只有以test開(kāi)頭的方法是測(cè)試方法。
運(yùn)行單元測(cè)試時(shí),可以在測(cè)試文件中加入:
if __name__ == '__main__':unittest.main()然后run。
另一種,在命令行中輸入命令:
$ python -m unittest mydict_test ..... ---------------------------------------------------------------------- Ran 5 tests in 0.000sOK第二種方法,可以一次運(yùn)行多個(gè)測(cè)試文件,比較方便。
setUp與tearDown:在每個(gè)測(cè)試方法前后分別被執(zhí)行,避免在測(cè)試代碼中重復(fù)加入代碼。
最后,單元測(cè)試要考慮到異常,代碼不能過(guò)于復(fù)雜,以免本身就有bug。
- 文檔測(cè)試
Python中可以提供實(shí)例文檔,在文件中編寫(xiě)特定格式的注釋,調(diào)用doctest判斷程序是否會(huì)像注釋中那樣的運(yùn)行。
class Dict(dict):'''Simple dict but also support access as x.y style.>>> d1 = Dict()>>> d1['x'] = 100>>> d1.x100>>> d1.y = 200>>> d1['y']200>>> d2 = Dict(a=1, b=2, c='3')>>> d2.c'3'>>> d2['empty']Traceback (most recent call last):...KeyError: 'empty'>>> d2.emptyTraceback (most recent call last):...AttributeError: 'Dict' object has no attribute 'empty''''def __init__(self, **kw):super(Dict, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Dict' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = valueif __name__=='__main__':import doctestdoctest.testmod()然后run。如果什么都沒(méi)輸出,就說(shuō)明編寫(xiě)的doctest運(yùn)行都是正確的。
??注:本文為學(xué)習(xí)廖雪峰Python入門(mén)整理后的筆記
轉(zhuǎn)載于:https://www.cnblogs.com/likely-kan/p/7517924.html
總結(jié)
以上是生活随笔為你收集整理的Day-10: 错误、调试和测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Newtonsoft.Json.dll
- 下一篇: 微信实现定位城市并获取城市编码