
Python Tutorial 第五堂(3)使用 assert 與 doctest
Python Tutorial 第五堂(2)表單與 CSRF << 前情 對於靜態定型語言(Statically-typing language),因為變數有型態資訊,因而編譯器等工具,可以在程式運行之前檢查出許多型態不正確的資訊。 Python 是動態定型語言(Dynamically-typing language),也就是說,在 Python 中變數沒有型態,只是用來作為參考實際物件的一根柄(Handle),如果有型態錯誤上的操作,基本上會是在執行時期運行至該段程式碼時,才會產生錯誤訊息,因此對於 Python 來說,檢查出型態不正確的任務,必須開發者本身來承擔,為程式設計測試程式,會是個不錯的方式之一。 (對於靜態定型語言,雖然有編譯器等工具,協助開發者於程式運行之前檢查型態錯誤問題,然而,設計優良測試程式檢測執行時期功能是否符合預期亦非常重要;對於動態語言,現在也有一些型態註解方案,可提供分析工具於程式運行前檢查型態資訊,像是 Python 的 PEP-3170 提出的 Function annotation。) 在 Python 的世界中,當然不乏撰寫測試的相關工具,像是 …
這一篇文章將會先介紹一下 python fibo.py 模組中的程式碼會像你執行 if __name__ == "__main__": 測試的程式碼 當你直接執行某個模組時, assert要在程式中安插斷言,使用 assert_stmt ::= "assert" expression ["," expression] 使用 if __debug__: if not expression: raise AssertionError 如果有兩個 if __debug__: if not expression1: raise AssertionError(expression2) 也就是說,第二個 expression 的結果,會被當作
那麼何時該使用斷言呢?…一般有幾個建議:
前置條件斷言的例子如下: def __set_refresh_Interval(interval): if interval > 0 and interval <= 1000 / MAX_REFRESH_RATE: raise ValueError('Illegal interval: ' + interval) # 函式中的程式流程 程式中的 def __set_refresh_Interval(rate): (assert interval > 0 and interval <= 1000 / MAX_REFRESH_RATE, 'Illegal interval: ' + interval) # 函式中的程式流程 (防禦式程式設計有些不好的名聲,不過並不是做了防禦式程式設計就不好,可以參考 避免隱藏錯誤的防禦性設計) 一個內部不變量的例子則是如下: if balance >= 10000: ... elif 10000 > balance >= 100: ... else: # balance 一定是少於 100 的情況 ... 如果要在 if balance >= 10000: ... else if 10000 > balance >= 100: ... else: assert balance < 100, balance ... 另一個情況是: if suit == Suit.CLUBS: ... elif suit == Suit.DIAMONDS: ... elif suit == Suit.HEARTS: ... elif suit == Suit.SPADES: ... 如果列舉檢查只會有以上四個條件,也可以運用斷言來實現速錯: if suit == Suit.CLUBS: ... elif suit == Suit.DIAMONDS: ... elif suit == Suit.HEARTS: ... elif suit == Suit.SPADES: ... else: assert False, suit 程式碼中有些一定不會執行到的流程區段,可以使用斷言來確保這些區段被執行時拋出錯誤。例如: def foo(list): for ele in list: if ...: return # 這邊應該永遠不會被執行到 可以改為: def foo(list): for ele in list: if ...: return assert False doctest
舉例來說,你也許為 util.py 中的 def sorted(xs, compare = ascending): ''' sorted(xs) -> new sorted list from xs' item in ascending order. sorted(xs, func) -> new sorted list. func should return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. >>> sorted([2, 1, 3, 6, 5]) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], ascending) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], descending) [6, 5, 3, 2, 1] >>> sorted([2, 1, 3, 6, 5], lambda a, b: a - b) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], lambda a, b: b - a) [6, 5, 3, 2, 1] ''' return [] if not xs else __select(xs, compare) 在同一個模組中,撰寫了以下的程式片段: if __name__ == '__main__': import doctest doctest.testmod() 那麼直接執行模組時,就會執行測試,加上 你也可以將這類文件寫在文字檔案中,例如一個 util_test.txt: The ``util`` module ====================== Using ``sorted`` ------------------- >>> from util import * >>> sorted([2, 1, 3, 6, 5]) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], ascending) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], descending) [6, 5, 3, 2, 1] >>> sorted([2, 1, 3, 6, 5], lambda a, b: a - b) [1, 2, 3, 5, 6] >>> sorted([2, 1, 3, 6, 5], lambda a, b: b - a) [6, 5, 3, 2, 1] 而 util.py 中改寫為以下,就可以從文字檔案中讀取內容並執行測試: if __name__ == '__main__': import doctest doctest.testfile(“util_test.txt") 你也可以直接執行 練習 14:使用 doctest請開啟 Lab 中 exercises/exercise14 的 util.py,將其中的兩行 if __name__ == '__main__': import doctest doctest.testmod() 然而在 python util.py python util.py -v 接著如上頭編輯 util_test.txt,完成後如下執行指令,看看會發生什麼事: python -m doctest util_test.txt python -m doctest –v util_test.txt 參考資料
|