最近在除錯python程式,對於這種動態語言,我之前的方法大多都是打tag,真是痛苦的要死。話說回來,debug是為了什麼?debug可以看成是對我們的猜測的一種驗證。如果我們能通過工具將需要的資訊(變數、堆疊)都顯示出來,除錯就很有效率。下面就介紹一個python除錯工具:pudb。
本文部分內容參考自Professor Norm Matloff的文章,我特此發了郵件給他徵得了翻譯的許可權。
Professor Norm Matloff, that is very nice of you, and thanks.
pudb是全屏的基於控制檯的視覺化偵錯程式。如果你還記得Turbo C的話,看到這個你應該會感到親切的。Homepage
先概要看一下pudb的特性:
- 原始碼語法高亮,棧、斷點、變數可見並且一直動態更新。變數展示還有很多可以定製化的功能。
- 基於鍵盤,簡單高效。為什麼說高效呢?因為它支援VI的滑鼠移動。還支援PDB的某些命令
- 支援查詢原始碼,可以使用m代用module browser檢視載入的模組
- 斷點設定:滑鼠移到某行程式碼,按b,然後可以在斷點視窗編輯斷點
- PuDB看重異常處理,post-mortem模式使折回到crash的最後一步更簡單
安裝
1 2 3 |
pip install pudb or easy_install pudb |
使用
為了支援pudb,需要在程式碼中插入
1 2 3 |
from pudb import set_trace; set_trace() or import pudb; pu.db |
然後通過下面命令啟動pudb
1 |
pudb my-script.py |
step by step
我們以下面這段二分程式binsearch.py為例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import pudb; pu.db def findinspt(x, xnew): n = len(x) lo = 0 hi = n-1 while True: mid = (lo + hi) / 2 if xnew > x[mid]: lo = mid + 1 else: hi = mid if xnew == x[mid]: return mid if __name__ == "__main__": y = [5,12,13] print findinspt(y,3) print findinspt(y,8) print findinspt(y,12) print findinspt(y,30) |
執行下面命令
1 |
pudb binsearch.py |
不出意外會得到下面的視窗,左半邊是原始碼,右邊一次是變數視窗、程式呼叫棧、斷點
先介紹幾個簡單命令,不需要記住,因為下面會多次提到
- n: next,也就是執行一步
- s: step into,進入函式內部
- c: continue
- b: break point,斷點
- !: python command line
- ?: help
最重要的是記住?,需要的時候按”?”查詢
我們按一下n會發現y=[5,12,13]這行程式碼高亮顯示了,表示執行到了這行程式碼。
再按一下n,y=[5,12,13]這行程式碼就執行完了,仔細看會發現右上角的變數視窗出現了y:list,表示程式記憶體中有了變數y。後面的List是type。
要檢視y的值,按s即可。但是如果這個時候你要是不小心按到了Enter,你會發現下面的介面。
對的,這就是有關變數的所有資訊。你只需要將游標移動到你要檢視的資訊,比如 Show repr(),按’Enter’鍵,再移動向右方向鍵到ok上,再按Enter鍵,就可以檢視相應的資訊的。但是如果你檢視了變數不存在的資訊,就會出現下面的畫面。
當游標停在函式上的時候可以選擇n執行到下一行程式碼,或者s進入函式內部。正常如果有終端輸出的時候,pudb會回到終端。但是我在mac上並沒有回去,有待進一步驗證。這個時候我們執行n的時候,如果函式有問題,會出現異常:[PROCESSING EXCEPTION – hit e to examine]。按e就會顯示異常的具體資訊。
下面就是進入函式進行除錯了。先按q選擇restart重啟pudb,執行到上面異常的哪一行選擇s進入函式內部。這個時候findinspet()函式內部的變數就會顯示在右上角變數視窗中。按n單步除錯一會,發現lo,hi都變成0了。說明出現問題了。
修改程式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import pudb; pu.db def findinspt(x, xnew): n = len(x) lo = 0 hi = n-1 while True: mid = (lo + hi) / 2 if xnew > x[mid]: lo = mid + 1 else: hi = mid if xnew == x[mid]: return mid if lo == hi: return lo if __name__ == "__main__": y = [5,12,13] print findinspt(y,3) print findinspt(y,8) print findinspt(y,12) print findinspt(y,30) |
重新啟動pudb。游標到main,按c會得到如下介面。
這時候選擇restart,然後按o就可以看到輸出結果了。
結果對於3,8,12都是對的,30的插入位置不對。為了讓debug效率更高,下面使用斷點功能。使用向下方向鍵,游標移動到第13行,按b即在該行設定了斷點,右下角的斷點顯示視窗可以看到。
但是我們只想debug當xnew等於30的時候的情況。先使用向右方向鍵,再使用向下方向鍵到斷點顯示視窗的斷點上,按Enter。下圖。在condition的右側設定條件xnew==30,點選OK儲存。
使用方向鍵切換到左側原始碼部分,先按n單步執行到main主程式,然後按c,程式就執行到了斷點位置處。從右側可以看到xnew的值為30。
這個時候就可以根據我們需要來單步除錯了。對於本文的程式,會發現需要對邊界條件處理一下。程式改為下面即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import pudb; pu.db def findinspt(x, xnew): n = len(x) lo = 0 hi = n-1 while True: mid = (lo + hi) / 2 if xnew > x[mid]: lo = mid + 1 else: hi = mid if xnew == x[mid]: return mid if lo == hi: if xnew <= x[lo]: return lo else: return lo if __name__ == "__main__": y = [5,12,13] print findinspt(y,3) print findinspt(y,8) print findinspt(y,12) print findinspt(y,30) |
到此,基本上涉及了Debug的一些主要情況。如果說最後的程式是一個產品,Debug就是產品出問題時候的用來解決問題的工具。使用工具的效率一定程度也決定了產品的生產效率。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式