pdb調試神器使用終極指南


pdb為python程序實現了一個交互式調試環境。它包括一些特性,可以暫停程序,查看變量值,以及逐步監視程序執行,從而能了解程序具體做了什么,並查找邏輯中存在的bug。

啟動調試工具

使用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):可以打印當前函數的所有參數的值。
ppp:這兩個是類似python的print和pprint的功能。輸出信息的,pp帶有美化功能。
!:在一個表達式前面加一個!,可以修改python程序當前正在運行的值,比如上面的例子num等於10,如果執行!num=3,后面再輸出num你會看到此時的num變成3了,這個可以減少測試時候我們的循環次數了。當然這個循環次數是否可以變動還是看程序的邏輯。

參考資料

https://docs.python.org/3.7/library/pdb.html
更多內容關注公眾號:python學習開發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM