Day-10: 错误、调试和测试
程序運行時,會遇到各種各樣的錯誤。
編寫錯誤叫做bug,而另一類由于運行過程中無法預測的,比如寫文件時,磁盤滿了,寫不進去;或者從網絡抓取數據,網絡突然掉了。這些錯誤稱為異常,程序中需要對異常進行處理,使得程序能夠運行下去。
- 錯誤處理
Python中,程序運行錯誤時,如果錯誤沒有捕獲,它會一直往上拋,最后被Python解釋器捕獲,打印一個錯誤。
# 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從上到下,錯誤會一層層的反饋,直到顯示最終出錯的地方。
try...except...finally...:常用這種方法來檢查錯誤并捕捉到,同時進行相應的處理。
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注意到,錯誤類型有很多種,它們其實都是從BaseException類派生出來的,常見的錯誤類型和繼承關系有:https://docs.python.org/2/library/exceptions.html#exception-hierarchy
- 調試
程序運行一次就成功的概率很小,基本上不超過1%。一般有如下的調試方法:
第一種,直接在可能出錯的地方print出來,但是后期會一個個刪掉。
第二種,使用斷言來代替。
# err.py def foo(s):n = int(s)assert n != 0, 'n is zero!'return 10 / ndef main():foo('0')斷言中,如果“n != 0”是錯的,就拋出AssertionError,并顯示后面的字符串。
第三種,使用logging。
logging有debug,info,warning,error等幾個級別,從前到后優先級依次提高,即如果,指定level=WARNING后,debug和info就不起作用了。這樣一樣,就可以輸出不同級別的信息,也不用刪除,最后統一控制輸出哪個級別的信息。
logging的另一個好處是通過簡單的配置,一條語句可以同時輸出到不同的地方,,比如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
第四種調試方式,就是調試器pdb,讓程序以單步方式運行,可以隨時查看運行狀態。
# err.py s = '0' n = int(s) print 10 / n然后,以參數-m pdb啟動,單步運行
$ 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單步運行下一步
(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 變量名來查看變量狀態。
(Pdb) p s '0' (Pdb) p n 0輸入q結束運行
(Pdb) n ZeroDivisionError: 'integer division or modulo by zero' > /Users/michael/Github/sicp/err.py(4)<module>() -> print 10 / n (Pdb) q另外在合適的地方,設置pdb.set_trace(),可以作為斷點。
# err.py import pdbs = '0' n = int(s) pdb.set_trace() # 運行到這里會自動暫停 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到達斷點時,進入pdb調試器。
最后,還有方便的IDE調試器。
- 單元測試
單元測試,顧名思義,就是對一個部分測試,可以是一個模塊、一個函數或者一個類。它的目的是保證該單元能夠實現原先規劃的功能,為之后的整體調試做準備。
例如,現有模塊mydict.py,對它的要求是實現如下功能:
>>> 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編寫的單元測試,需要引入unittest模塊,編寫的mydict_test.py如下:
import unittestfrom mydict import Dictclass TestDict(unittest.TestCase):def test_init(self): # 測試初始化功能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): # 測試key的功能d = Dict()d['key'] = 'value'self.assertEquals(d.key, 'value')def test_attr(self): # 測試屬性功能d = Dict()d.key = 'value'self.assertTrue('key' in d)self.assertEquals(d['key'], 'value')def test_keyerror(self): # 測試key錯誤的功能d = Dict()with self.assertRaises(KeyError):value = d['empty']def test_attrerror(self): # 測試屬性錯誤的功能d = Dict()with self.assertRaises(AttributeError):value = d.empty編寫的單元測試類,從unittest.TestCase繼承。其中,只有以test開頭的方法是測試方法。
運行單元測試時,可以在測試文件中加入:
if __name__ == '__main__':unittest.main()然后run。
另一種,在命令行中輸入命令:
$ python -m unittest mydict_test ..... ---------------------------------------------------------------------- Ran 5 tests in 0.000sOK第二種方法,可以一次運行多個測試文件,比較方便。
setUp與tearDown:在每個測試方法前后分別被執行,避免在測試代碼中重復加入代碼。
最后,單元測試要考慮到異常,代碼不能過于復雜,以免本身就有bug。
- 文檔測試
Python中可以提供實例文檔,在文件中編寫特定格式的注釋,調用doctest判斷程序是否會像注釋中那樣的運行。
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。如果什么都沒輸出,就說明編寫的doctest運行都是正確的。
??注:本文為學習廖雪峰Python入門整理后的筆記
轉載于:https://www.cnblogs.com/likely-kan/p/7517924.html
總結
以上是生活随笔為你收集整理的Day-10: 错误、调试和测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Newtonsoft.Json.dll
- 下一篇: Android 日夜间切换Demo