三步學會 Debug 神器,效率提升不少!
PDB是Python自帶的一個包,為Python程式提供了一種互動的原始碼除錯功能,主要特性包括設定斷點、單步除錯、進入函式除錯、檢視當前程式碼、檢視棧片段、動態改變變數的值等。
啟動除錯工具
使用PDB的第一步是讓直譯器在適當的時候進入除錯工具。可以採用很多不同的方法
達到這個目的,具體取決於起始條件和所要除錯的內容。
從命令列執行:
PDB的最直接的方式是通過命令列執行,命令格式如下:
python3 -m pdb py檔案
這裡我們來個小例子,有一個檔名為1.py,程式碼如下:
def foo(num): print(f"當前的數字是:{num}") if __name__ == '__main__': foo(3)
然後再命令列中定位到當前位置。

通過命令列執行PDB
通常PDB在列印一個檔名時會在輸出中包含各模組的完整路徑,然後會顯示出下一行將要執行的程式碼。這個裡的話就是接下來將執行函式foo了。
在直譯器中執行:
如果在互動式環境下執行除錯工具,可以使用run()或者runeval()。
在程式中執行:
上面的情況只是適合一開始就啟動除錯工具,對於長時間的而且需要執行一段時間才去進行除錯的情況,更方便的做法就是在需要執行的程式碼之前加set_trace()方法了。
set_trace()可以在任意位置進行呼叫,像下面的例子。
import pdb def foo(num): print(f"當前的數字是:{num}") for i in range(num): pdb.set_trace() print(f"當前迴圈的數字:{i}") if __name__ == '__main__': foo(10)
上面的程式碼在for迴圈中打了一個斷點,可以發現我們程式停在了for迴圈的位置,然後顯示出下一行將要執行的內容
print(f"當前迴圈的數字:{i}")
怎麼知道迴圈到第幾個數字呢?
答:通過直接輸入變數i可以看到當前的值。
怎麼知道當前的程式碼執行到哪了?
答:通過命令where(簡寫w)可以得出正在執行哪一行,以及程式的呼叫棧的位置。
如何檢視附近當然程式碼附近的程式碼資訊?
答:通過list(簡寫l)可以看周圍的程式碼預設是上5行、下5行。另外命令longlist(簡寫ll)可以輸出當前的函式的原始碼。如果需要看整個類的原始碼可以通過source+類名獲取原始碼。


常用命令
步驟執行:
step(簡稱s):函式單步執行,如果遇到函式會進入函式內部繼續除錯,如果不需要進入函式體只是一步一步執行,此時就要用下面的next了。
next(簡稱n):單步執行命令,不會進入函式體,但是向之前說的那個如果遇到了一個for迴圈10次還好如果是10000次呢,此時就要用到下面的命令了。
until:該命令型別next,只不過它會繼續執行,直到執行到同一個函式中行號大於當前值的一行,也就是說可以用until跳出迴圈末尾。當然until也可以指定一個比當前行號大的值,調到指定位置。
一個例子:
import pdb class Myobj(): def foo(self,num): print(f"當前的數字是:{num}") pdb.set_trace() for i in range(num): print(f"當前迴圈的數字:{i}") print("over") if __name__ == '__main__': m=Myobj() m.foo(4)
可以發現執行until把整個迴圈走了一遍,然後到下一行也就是。
-> print("over")
return:return也可以繞開一段程式碼的捷徑,只不過它會繼續執行。
直至函式準備執行一個return語句,然後會暫停,使得在函式返回之前可以看到返回值。
一個沒什麼實際用途的例子,不過可以很好地演示這個效果。
import pdb lst=[] class Myobj(): def foo(self,num): print(f"當前的數字是:{num}") pdb.set_trace() for i in range(num): print(f"當前迴圈的數字:{i}") lst.append(i) return lst if __name__ == '__main__': m=Myobj() m.foo(4)
斷點相關:
break(簡稱b): 當然隨著程式碼的增長即使使用return和until或者next都很費時間,此時就要考慮在指定位置設定斷點的方式了,如果要在檔案的一個特定行設定斷點,可以使用break lineno,然後通過下面的continue(簡寫c)命令調到下一個斷點。
我們還可以指定在某個函式中設定斷點比如:break Myobj().foo。
除此之外還可以執行其他檔案設定斷點,也可以相當於sys.path上將某個檔案的相對路徑。如果只執行break命令可以看到哪些地方有斷點,包括哪個檔案行號等資訊。
disable:可以指定上面break之後顯示的斷點,執行後可以發現之前Enb欄有yes變為false。此時輸入l可以看到打斷點的為會有B標識。
如果想徹底刪除就需要執行clear命令了。

新增和刪除斷點效果展示圖
clear:徹底刪除一個斷點,使用方式clear id號,型別disable。
tbreak:臨時斷點,程式第一次執行到臨時斷點時會自動清除。不用再去手動刪除了。
條件斷點:
可以對斷點應用一些規則,以便其僅當條件滿足時才執行。與手動啟用和禁用斷點相比,使用條件斷點可以更好地控制偵錯程式暫停程式的方式。
條件斷點可以通過兩種方式設定。第一種方法是指定使用break設定斷點時的條件。使用方法是程式碼行號加表示式。看一個應用例子。
import pdb lst = [] class Myobj(): pdb.set_trace() def foo(self, num): print(f"當前的數字是:{num}") if __name__ == '__main__': m = Myobj() [m.foo(i) for i in range(10)]
解析下圖中命令的含義:
1.break 10,num>5,是指在第10行打斷點,然後條件是num>5的時候,通過後面輸出break可以看到具體的斷點資訊,很明顯看到我們的斷點條件。
stop only if num>5
2.如果表示式的計算結果為true,則執行將在斷點處停止。
除此之外,還可以使用條件命令將條件應用於現有斷點。引數是斷點id和表示式。
還是上面的程式碼讓我們看效果圖。

忽略斷點
如果在迴圈的過程中想忽略前幾條結果,比如這裡忽略前3個,就可以使用ignore。
使用方法是:
ignore 斷點id 忽略次數。
如果在執行之前不想忽略了可以使用下面命令,如果已經執行continue了的話就沒效果了。
ignore 斷點id 0
監視變數:
display:有時候我們需要實時觀察一個變數的變化,這個時候dispaly就是最好的幫手,如果想移出可以使用undisplay。

display監視變數
改變工作流:
jump:jump命令在執行時改變程式的流程,而不修改程式碼。 它可以向前跳過以避免執行某些程式碼,也可以向後跳轉以再次執行它。
import pdb def f(n): pdb.set_trace() result = [] j = 0 for i in range(n): j = i * n + j j += n result.append(j) return result if __name__ == '__main__': print(f(5))
向前跳:
向前跳轉會將執行點移動到當前位置之後,而不會執行期間任何語句。

向後跳:
跳轉還可以將程式執行移動到已經執行的語句中,以便再次執行它。
不允許的jump方式
1.跳入和跳出某些流控制語句,無法判斷什麼時候進入。
2.跳轉可以用來輸入函式,但是不給引數,程式碼也不能工作。
3.跳轉不會進入for迴圈或try:except語句等塊的中間。
4.finally塊中的程式碼必須全部執行,因此跳轉不會離開該塊。
5.最基本的限制是跳轉被限制在呼叫堆疊的底部框架上。 向上移動堆疊以檢查變數後,此時無法更改執行流程。
其他命令
-
up(簡稱u):可以向棧中較舊的幀移動
-
down(簡稱d) : 可以向棧中較新的幀移動。 每次在棧中上移或者下移時,除錯工具都會列印當前位置,格式與where生成的格式相同。
-
args(簡稱a) : 可以列印當前函式的所有引數的值。
-
p和pp : 這兩個是類似python的print和pprint的功能。輸出資訊的,pp帶有美化功能。
-
! : 在一個表示式前面加一個!,可以修改python程式當前正在執行的值,比如上面的例子num等於10,如果執行!num=3,後面再輸出num你會看到此時的num變成3了,這個可以減少測試時候我們的迴圈次數了。當然這個迴圈次數是否可以變動還是看程式的邏輯。
-
參考資料
https://docs.python.org/3.7/library/pdb.html
作者簡介:陳祥安,高階爬蟲工程師,從業多年,有著豐富的爬蟲工作經驗,從c#語言轉入到Python語言,擅長各種爬蟲技術,熟悉大規模爬蟲開發,熱愛並喜歡鑽研 P ython。歡迎大家和我一起來交流學習。
本文系作者投稿,版權歸作者所有。
【END】
作為碼一代,想教碼二代卻無從下手:
聽說少兒程式設計很火,可它有哪些好處呢?
孩子多大開始學習比較好呢?又該如何學習呢?
最新的程式設計教育政策又有哪些呢?
下面給大家介紹CSDN新成員: 極客寶寶(ID: geek_baby)
戳他了解更多↓↓↓
熱 文推 薦
☞ 前端程式碼的整潔之道 | 技術頭條
☞ 她說:為啥程式設計師都特想要機械鍵盤?這答案我服!
System.out.println("點個在看吧!"); console.log("點個在看吧!"); print("點個在看吧!"); printf("點個在看吧!\n"); cout << "點個在看吧!" << endl; Console.WriteLine("點個在看吧!"); Response.Write("點個在看吧!"); alert("點個在看吧!") echo "點個在看吧!"
你點的每個“在看”,我都認真當成了喜歡