Python中什麼時候使用斷言 assert

 

 

 

使用斷言的最佳時機偶爾會被提起,一般是由於有人誤用,所以我以爲有必要寫一篇文章來闡述一下何時應該用斷言,爲何應該用,何時不應用。程序員

對那些沒有意識到用斷言的最佳時機的人來講,Python的斷言就是檢測一個條件,若是條件爲真,它什麼都不作;反之它觸發一個帶可選錯誤信息的AssertionError。以下例所示:數據庫

不少人將斷言做爲當傳遞了錯誤的參數值時的一種快速而簡便的觸發異常的方式。但實際上這是錯誤的,並且是很是危險的錯誤,緣由有兩點。首先,AssertionError一般是在測試函數參數時給出的錯誤。你不會像下面這樣編碼:編程

你應該用TypeError來替代,「斷言」解決了錯誤的異常類型。函數

可是對斷言來講更危險也更糾結的是:若是你執行Python時使用了-O或-OO優化標識,這可以經過編譯卻歷來不會被執行,實際上就是說並不能保證斷言會被執行。當恰當地使用了斷言,這很是好的,但當不恰當地使用了斷言,在使用-O標識執行時它將致使代碼被完全中斷。單元測試

那麼咱們何時應該使用斷言呢?若是沒有特別的目的,斷言應該用於以下狀況:測試

  • 防護性的編程
  • 運行時對程序邏輯的檢測
  • 合約性檢查(好比前置條件,後置條件)
  • 程序中的常量
  • 檢查文檔

(斷言也能夠用於代碼測試,用做一個作事毛手毛腳的開發人員的單元測試,只要能你接受當使用-O標誌時這個測試什麼都不作。我有時也會在代碼中用"assert Fasle"來對尚未實現的分支做標記,固然我但願他們失敗。若是稍微更細節一些,或許觸發NotImplementedError是更好的選擇)優化

由於程序員是對於代碼正確性表現出的信心不一樣,所以對於何時使用斷言的意見各不相同。若是你確信代碼是正確的,那麼斷言沒有任何意義,由於它們從不會失敗,所以你能夠放心地移除它們。若是你確信它們會失敗(例如對用戶輸入的數據的檢測),你不敢用斷言,這樣編譯就能經過,但你跳過了你的檢查。編碼

在以上兩種狀況之間的狀況就顯得特別有趣了,那就是當你相信代碼是正確的,但又不是特別肯定的時候。或許你忘記了一些奇怪的邊角狀況(由於咱們都是人),在這種狀況下,額外的運行時檢查將幫助你儘量早地捕獲錯誤,而不是寫了一大堆代碼以後。設計

(這就是爲何使用斷言的時機會不一樣。由於咱們對代碼正確性的信息不一樣,對於一我的有用的斷言,對於另外一我的來講倒是無用的運行時測試。)code

另外一個斷言用得好的地方就是檢查程序中的不變量。一個不變量是一些你能相信爲真的條件,除非一個缺陷致使它變成假。若是有一個缺陷,越早發現越好,所以咱們須要對其進行測試,但咱們不想由於這些測試而影響代碼執行速度。所以採用斷言,它能在開發時生效而在產品中失效。

一個關於不變量的例子多是這樣的狀況。若是你的函數在開始的時候指望一個打開的數據庫鏈接,而且在函數返回後該數據庫鏈接依然是打開的,這是一個函數的不變量:

斷言也是一個很好的檢查點註釋。爲了替代以下注釋:

#當咱們執行到這裏,咱們知道n>2

你能夠確保在運行時用如下斷言:

斷言也是一種防護性的編程形式。你不是在防範當前代碼發生錯誤,而防範因爲之後的代碼變動發生錯誤。理想狀況下,單元測試應該直到這個做用,可是讓咱們面對這樣一個現實:即便存在單元測試,他們在一般狀況下也不是很完備。內建的機器人可能沒有工做,但數週以來也沒有人注意到它,或者人們在提交代碼以前忘記了執行測試。內部檢查將是防止錯誤滲入的另外一道防線,尤爲對於那些悄悄地失敗,但會引發代碼功能錯誤並返回錯誤結果的狀況有效。

假設你有一系列的if...elif代碼塊,你預先知道變量指望的值:

假設這段代碼如今徹底正確。但它會一直正確嗎?需求變動,代碼變動。若是需求變爲容許target = w,並關聯到run_w_code,那將會發生什麼狀況?若是咱們變動了設置target的代碼,可是忘記了改變這個代碼塊,它就會錯誤地調用run_z_code(),錯誤就會發生。對於這段代碼最好的方法就是編寫一些防護性的檢查,這樣它的執行,即便在變動之後,要麼正確,要麼立刻失敗。

在代碼開始添加註釋是個好的開端,可是人們都不太喜歡讀和更新這些註釋,這些註釋會很快變得過期。但對於斷言,咱們能夠同時對這塊代碼編寫文檔,若是這些斷言被違反了,會直接引發一個簡單而又直接的失敗。

這裏的斷言同時用於防護性編程和檢查文檔。我認爲這是最優的解決方案:

這誘使開發者去不理代碼,移除像value ==c這類沒必要要的測試,以及RuntimeError的「死代碼」。另外,當"unexpected error"錯誤發生時這個消息將很是窘迫,確實會發生。

合約式設計是斷言另外一個用得好的地方。在合約式設計中,咱們認爲函數與其餘調用者遵循合約,例如像這樣的狀況:

「若是你傳給我一個非空字符串,我保證返回轉換成大寫的首字母。」

若是合約被破壞了,不論是被函數自己仍是調用者,這都會產生缺陷。咱們說這個函數須要有前置條件(對指望的參數的限制)和後置條件(對返回結果的約束)。所以這個函數多是這樣的:

合約式設計的目的是,在一個正確的程序裏,全部的前置條件和後置條件都將獲得處理。這是斷言的經典應用,自(這個想法持續)咱們發佈完好陷的程序而且將其放入產品,程序將是正確的而且咱們能夠放心地移除檢查。

這裏是我建議不使用斷言的狀況:

*不要用於測試用戶提供的數據,或者那些須要在全部狀況下須要改變檢查的地方

*不要用於檢查你認爲在一般使用中可能失敗的地方。斷言用於很是特別的失敗條件。你的用戶毫不看到一個AssertionError,若是看到了,那就是個必須修復的缺陷。

*特別地不要由於斷言只是比一個明確的測試加一個觸發異常矮小而使用它。斷言不是懶惰的代碼編寫者的捷徑。

*不要將斷言用於公共函數庫輸入參數的檢查,由於你不能控制調用者,而且不能保證它不破壞函數的合約。

*不要將斷言用於你指望修改的任何錯誤。換句話,你沒有任何理由在產品代碼捕獲一個AssertionError異常。

*不要太多使用斷言,它們使代碼變得晦澀難懂。

相關文章
相關標籤/搜索