使用PDB調試Python程序
學習編程,調試是寫過1000行代碼后必須掌握的技能,很多同學因為不會調試程序或者沒有調試過問題,技能就停止不前了。
學習程序設計時,我提倡在Linux下使用命令行編譯,運行,調試程序。學習C語言要掌握GDB,學習Java要掌握JDB,本文是《使用JDB調試Java程序》的Python版本。我在課堂上講解過使用VSCode調試程序,這是命令行版本。
Python程序中有邏輯錯誤,就需要使用Pdb來調試了。調試程序在IDE中很方便,但是我們在Linux環境下學習Python,那就需要在命令行下使用Pdb進行調試了。
Pdb不但是個好的調試工具,也是一個好的學習工具,可以讓你了解程序的動態執行過程。
學習建議:Linux Bash下打開三個標簽頁
我們提倡在Linux命令行下學習Python編程。學習時在Ubuntu Bash中通過Ctrl+Alt+T
和Ctrl+Shift+T
快捷鍵打開三個標簽(tab):一個使用vim編輯代碼;一個使用python3編譯運行代碼;一個使用PDB調試代碼。
如下圖所示,這樣就不用在一個窗口中進行編輯、編譯運行和調試的切換了,能提高效率。
如上圖, 我們在Linux Bash中輸入 vim HelloPDB.py
編輯調試示例代碼:
def main():
i = 5
j = 6
sum = add(i, j)
print(sum)
sum = 0
for i in range(0,101):
sum = sum + i
print(sum)
def add(augend, addend):
sum = augend +addend
return sum
if __name__ == '__main__':
main()
代碼編輯完,我們按 “:w” 進行保存而不是 “:wq” 進行保存退出,這樣在編譯或調試中遇到問題就可以按Alt+1
進入第一個標簽修代碼了。
我們按Alt+2
進入第二個標簽,使用python3 HelloPDB.py
編譯運行程序。
我們按Alt+3
進入第三個標簽,使用python3 -m pdb HelloPDB.py
對程序進行調試。
調試基礎
調試程序先要學會設置斷點,這樣才能讓程序停在你感覺有問題的代碼處進行排查。學習調試我們要學會設置四種斷點:
- 行斷點
- 函數斷點
- 條件斷點
- 臨時斷點
我們在PDB中輸入 h
或 help
可以查看命令列表:
上圖中的b
、break
、tbreak
和condition
命令是與設置斷點相關的四個常用命令,其中b
是break
的縮寫,可以相互替代。
-> def main():
表示Pdb定位了到下一步要執行的代碼def main()
。
我們通過運行b main
命令在main函數開始處設置斷點:
如上圖,我們輸入r
命令運行程序,程序會在main函數的第一行i=5
處停下。
可以使用step
命令運行main函數下一步代碼i=5
,使用p
或pp
命令查看變量i
的值。
此時,由於還未運行j=6
這一行代碼,所以無法查看變量j
的值。
我們可以使用l
或list
或ll
(l
和list
打印附近11行源代碼,ll
打印全部源代碼)來查看運行到了源代碼的什么位置,下圖中的->
指示出代碼運行到了哪一行:
這里要注意上圖是將要運行第4行,但還沒有運行。還要注意,這一行是個函數調用。我們繼續輸入step
和list
,我們發現代碼跳入13行函數體中了:
除了p
命令,我們還可以使用a
或者args
命令查看當前函數的參數列表。
一般說來,調試時遇到函數調用,我們先看調用結果對不對,結果正確,說明函數沒有問題,就不用進入函數體了;如果函數調用結果不對,我們才需要進入函數體進行調試。單步跟蹤命令next
和step
在執行一般語句時沒有區別,在執行有函數調用的語句時,next
會把函數執行完,step
會進入函數體。所以在調試時,單步執行我們要優先使用next
,這樣效率比較高。
現在已經進入函數體了,我們可以運行up
把函數執行完,返回到調用處,在這之后執行一般語句,你會發現next
和step
沒有區別。
第8行和第9行是個循環,這兩條語句單步執行起來有點費勁。
我們可以通過b 11
在第11行設個斷點,然后運行c
或cont
命令就會一下子把循環運行完並停在第十二行。cont是continue的縮寫,功能是運行到下一個斷點處停止。
我們可以用break
命令查看設置的斷點的情況。
其實,這里最好設置的是臨時斷點tbreak
——在第一次執行到這個斷點之后,就自動刪除這個斷點,用法和b
一樣。
還有,如果第8行問題出在i=80處,我們就需要使用條件斷點。
條件斷點的設置方法是,需要先設置一個斷點或臨時斷點,然后引用該斷點的編號breaknumber,設置條件語句。
這里我重新啟動了Pdb,我在第9行設置了一個臨時斷點,它的斷點編號breaknumber是3,我希望在i=80的時候停下來,那么根據condition breaknumber condition
的輸入規則,我們只要輸入condition 3 i==80
即可,當判斷i==80返回True的時候,就會接受斷點。
繼續運行下去在第9行停了下來,這個時候可以查看i
的值為80
這個時候再查看現有斷點,可以看到第9行的臨時斷點已經被刪除了。
最后,我們使用q
或quit
可以退出Pdb。
類的調試——遞歸的學習
遞歸算法是一種直接或間接地調用自身的算法。在編寫程序時,遞歸算法對解決一大類問題是十分有效的,它往往使算法的描述簡潔而且易於理解。
遞歸用於解決形式相同,規模不同的問題,能用遞歸解決的問題都可以轉化為循環。遞歸把一個大型復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需少量的程序就可描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量。遞歸的能力在於用有限的語句來定義對象的無限集合。用遞歸思想寫出的程序往往十分簡潔易懂。
遞歸程序有兩個要點:遞歸公式和結束條件。我們以求整數的階乘為例:
有了公式,代碼就容易寫出來了:
class Factorial:
def fact(self, n):
if n==0:
return 1
else:
return n * self.fact(n-1)
def main():
my = Factorial()
print(my.fact(5))
main()
fact(5)的遞推過程如下圖:
進入調試,我們設置好斷點,開始運行:
類的方法調用一次就會形成一個棧幀,我們在Pdb中用where
顯示棧幀,用up
,down
可以在棧幀之間跳轉。
大家用四次up
,四次down
體會一下壓棧,出棧,其中>
顯示了棧幀位置:
感謝謝繹同學幫忙改寫Pyhton版本.
參考資料
歡迎關注“rocedu”微信公眾號(手機上長按二維碼)
做中教,做中學,實踐中共同進步!
-
版權聲明:自由轉載-非商用-非衍生-保持署名| Creative Commons BY-NC-ND 3.0
如果你覺得本文對你有幫助,請點一下左下角的“好文要頂”和“收藏該文”