初學時大多使用print或log調試程序,這在小規模的程序下很方便
但是更好的方法是一邊運行一邊檢查里面的變量和方法
1.Pdb
Pdb是一個交互式的調試工具,集成於Python標准庫中
Pdb能讓你根據需求跳轉到任意的Python代碼斷點、查看任意變量、單步執行代碼,甚至還能修改變量的值,而不必重啟程序
⚠️pdb 調試有個明顯的缺陷就是對於多線程,遠程調試等支持得不夠好,同時沒有較為直觀的界面顯示,不太適合大型的 python 項目。
而在較大的 python 項目中,這些調試需求比較常見,因此需要使用更為高級的調試工具,如PyCharm IDE。
手冊:https://docs.python.org/3.5/library/pdb.html#pdbcommand-where
pdb的使用方式和ipdb是一樣的
2.ipdb
ipdb是增強版的pdb,參考https://github.com/gotcha/ipdb
1)安裝:
(deeplearning) userdeMacBook-Pro:dogcat-6 user$ pip install ipdb Requirement already satisfied: ipdb in /anaconda3/envs/deeplearning/lib/python3.6/site-packages (0.12) ...
ipdb提供了調試模式下的代碼自動補全,還具有更好的語法高亮和代碼溯源,以及更好的內省功能。它與pdb接口完全兼容
2)結合PyTorch和ipdb進行調試
1》集成到源代碼中
要使用ipdb,只需要在想要進行調試的地方插入語句:
import ipdb
ipdb.set_trace() #相當於添加斷點
當代碼運行到這里,就會自動進入交互式調試模式
上面的方式雖然簡單,但是存在着兩個較為比較明顯的問題:
- 插入的斷點代碼會污染原來的代碼空間
- 每次插入斷點都需要修改源碼
try: import ipdb except: import pdb as ipdb def sum(x): r = 0 for ii in x: r += ii return r def mul(x): r =1 for ii in x: r *= ii return r ipdb.set_trace() x = [1,2,3,4,5] r = sum(x) r = mul(x)
當程序運行到ipdb.set_trace()語句時,會自動進入debug模式,在該模式中,我們可使用調試命令,如next或縮寫n實現單步執行;也可以查看Python變量,或是運行Python代碼
如果Python變量名和調試命令沖突,需在變量名前加!,這樣ipdb會執行對應的Python命令,而不是調試命令
下面舉例說明ipdb的調試
這里重點講解ipdb的兩大功能:
- 查看:在函數調用堆棧中自由跳動,並查看函數的局部變量
- 修改:修改程序中的變量,並能以此影響程序的運行結果
將上面的命令生成ipdb_test.py文件,使用命令行進行調試
(deeplearning) userdeMacBook-Pro:pytorch-learning user$ python ipdb_test.py > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(19)<module>() 18 ipdb.set_trace() ---> 19 x = [1,2,3,4,5] 20 r = sum(x)
--->光標所指處為運行到的代碼
下面進行說明調試時能夠使用的命令:
l 1,18 : list 1,18的縮寫,查看第1行到18行的代碼
ipdb> l 1,18 1 try: 2 import ipdb 3 except: 4 import pdb as ipdb 5 6 def sum(x): 7 r = 0 8 for ii in x: 9 r += ii 10 return r 11 12 def mul(x): 13 r =1 14 for ii in x: 15 r *= ii 16 return r 17 18 ipdb.set_trace()
n : next的縮寫,執行下一步;如果當前語句有一個函數調用,用 n 是不會進入被調用的函數體中的
ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(20)<module>() 19 x = [1,2,3,4,5] ---> 20 r = sum(x) 21 r = mul(x)
s : step的縮寫,進入函數sum內部
ipdb> s --Call-- > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(6)sum() 5 ----> 6 def sum(x): 7 r = 0
再繼續單步執行,再在函數中繼續執行,耶可以不用輸入n,直接回車則會使用上次的命令
ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(7)sum() 6 def sum(x): ----> 7 r = 0 8 for ii in x: ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(8)sum() 7 r = 0 ----> 8 for ii in x: 9 r += ii ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(9)sum() 8 for ii in x: ----> 9 r += ii 10 return r
u : up的縮寫,跳回上一層的調用
ipdb> u > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(20)<module>() 19 x = [1,2,3,4,5] ---> 20 r = sum(x) 21 r = mul(x)
d : down的縮寫,跳回之前調用到的下一層的位置
ipdb> d > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(9)sum() 8 for ii in x: ----> 9 r += ii 10 return r
當查看變量的命令和調試命令起沖突時,在前面加一個!
查看r變量,該變量名與調試命令r(eturn) 起沖突
ipdb> !r 0
return : 繼續運行,直到函數返回,結束該sum()函數的運算
ipdb> return --Return-- 15 > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(10)sum() 9 r += ii ---> 10 return r 11
當命令和調試命令沒有沖突的時候,可以直接運行,不用添加!
這里查看變量x的值,並對變量值進行修改
ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(21)<module>() 19 x = [1,2,3,4,5] 20 r = sum(x) ---> 21 r = mul(x) ipdb> x [1, 2, 3, 4, 5]
ipdb> x[0] = 10000
b 13 : break的縮寫,在13行處添加一個斷點,如果沒有添加位置則輸出所有的斷點信息
ipdb> b 13 Breakpoint 1 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:13
c : continue的縮寫,繼續運行,直到遇到斷點
ipdb> c > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(13)mul() 12 def mul(x): 1--> 13 r =1 14 for ii in x: ipdb> return --Return-- 1200000 > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(16)mul() 15 r *= ii ---> 16 return r 17
return后得到的是修改后的x的乘積
l(ist) 或 ll : ll
是查看整個源碼文件,l
可指定需要查看的行數,默認是當前往后 11 行,也可指定具體的范圍
ipdb> list 11 12 def mul(x): 1 13 r =1 14 for ii in x: 15 r *= ii ---> 16 return r 17 18 ipdb.set_trace() 19 x = [1,2,3,4,5] 20 r = sum(x) 21 r = mul(x)
pp 或 p expression: 打印變量的值,兩者的不同是p用的是print(),pp用的是pprint()
ipdb> pp x [10000, 2, 3, 4, 5] ipdb> x [10000, 2, 3, 4, 5]
exit 或 q(uit) : 中止並退出
ipdb> q
Exiting Debugger.
關於ipdb的使用還又一些技巧:
- <tab>鍵能夠自動補齊,補齊用法與IPython中的類似
- 可以直接在ipdb中修改變量的值
- h(elp)能夠查看調試命令的用法,比如h h可以查看h(elp)命令的用法,h jump能夠查看j(ump)命令的用法
其他命令:
run 或 restart
[args ...]
:兩者就是別名的關系,重新運行該python調試項目,如果提供了參數,會使用shlex進行分離,並將結果作為新的sys.argv。歷史記錄、斷點、行動和調試選項都會保留。
run [args ...]:這里的參數會作為運行腳本的參數
(deeplearning) userdeMBP:pytorch-learning user$ python -m ipdb ipdb_test.py /anaconda3/envs/deeplearning/lib/python3.6/runpy.py:125: RuntimeWarning: 'ipdb.__main__' found in sys.modules after import of package 'ipdb', but prior to execution of 'ipdb.__main__'; this may result in unpredictable behaviour warn(RuntimeWarning(msg)) > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(1)<module>() ----> 1 def sum(x): 2 r = 0 3 for ii in x: ipdb> run --x [3,3,3,3] Restarting ipdb_test.py with arguments: --x [3,3,3,3] > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(1)<module>() ----> 1 def sum(x): 2 r = 0 3 for ii in x: ipdb> import sys ipdb> sys.argv ['ipdb_test.py', '--x', '[3,3,3,3]']
看到上面的結果,等價於 python ipdb_test.py --x [3,3,3,3],這個參數會作為運行sum(x)的參數
如果要得到的是全新的調試器,使用exit 或 q(uit)
通過b設置的斷點在重新運行 debug 程序 (命令 restart
或 run
) 后會依然保留,如果要忽略這些斷點,有兩種做法:
- cl(ear) :如果后面帶有參數,就是清除指定的斷點;如果不帶參數就是清除所有的斷點
(deeplearning) userdeMacBook-Pro:pytorch-learning user$ python ipdb_test.py > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(19)<module>() 18 ipdb.set_trace() ---> 19 x = [1,2,3,4,5] 20 r = sum(x) ipdb> b 8 Breakpoint 1 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:8 ipdb> cl 1 Deleted breakpoint 1 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:8 ipdb> c (deeplearning) userdeMacBook-Pro:pytorch-learning user$
因為設置的斷點被清除了,所以運行c會直接結束
- disable/enable :禁用/激活斷點
(deeplearning) userdeMacBook-Pro:pytorch-learning user$ python ipdb_test.py > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(19)<module>() 18 ipdb.set_trace() ---> 19 x = [1,2,3,4,5] 20 r = sum(x) ipdb> b 8 Breakpoint 1 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:8 ipdb> b 9 Breakpoint 2 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:9 ipdb> disable 1 Disabled breakpoint 1 at /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py:8 ipdb> c > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(9)sum() 1 8 for ii in x: 2---> 9 r += ii 10 return r ipdb>
因為禁用了8處的斷點,所以運行c直接就到了9的斷點處
j(ump) :讓程序跳轉到指定的行數 ,能夠跳過中間某些行代碼的執行
注意:但是必須跳轉的地方在當前的代碼塊中
ipdb> j 13 *** Jump failed: line 13 comes after the current code block ipdb> j 10 > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(10)sum() 2 9 r += ii ---> 10 return r 11 ipdb>
w(here) : 顯示最近的一些棧幀信息
ipdb> w /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(20)<module>() 19 x = [1,2,3,4,5] ---> 20 r = sum(x) 21 r = mul(x) > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(10)sum() 2 9 r += ii ---> 10 return r 11
a(rgs) : 返回目前函數的參數列表
ipdb> a x = [1, 2, 3, 4, 5]
這個sum()函數現在的參數就只有一個x
2》通過命令行進行交互
這種方法與上面方法不同在於不需要在代碼中插入斷點語句,而是在運行時添加-m參數運行,然后再進行調試
生成ipdb_test_command.py:
#import ipdb def sum(x): r = 0 for ii in x: r += ii return r def mul(x): r =1 for ii in x: r *= ii return r #ipdb.set_trace() x = [1,2,3,4,5] r = sum(x) r = mul(x)
運行:
(deeplearning) userdeMacBook-Pro:pytorch-learning user$ python -m ipdb ipdb_test_command.py /anaconda3/envs/deeplearning/lib/python3.6/runpy.py:125: RuntimeWarning: 'ipdb.__main__' found in sys.modules after import of package 'ipdb', but prior to execution of 'ipdb.__main__'; this may result in unpredictable behaviour warn(RuntimeWarning(msg)) > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(2)<module>() 1 #import ipdb ----> 2 def sum(x): 3 r = 0 ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(8)<module>() 7 ----> 8 def mul(x): 9 r =1 ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(15)<module>() 14 #ipdb.set_trace() ---> 15 x = [1,2,3,4,5] 16 r = sum(x) ipdb> n > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(16)<module>() 15 x = [1,2,3,4,5] ---> 16 r = sum(x) 17 r = mul(x) ipdb> s --Call-- > /Users/user/pytorch/jupyter/pytorch-learning/ipdb_test.py(2)sum() 1 #import ipdb ----> 2 def sum(x): 3 r = 0 ipdb>
-m參數,這樣調用ipdb_test_command.py的話斷點就是程序的執行第一行之前
然后就可以和上面一樣使用命令進行調試
3.支持的函數
參考https://docs.python.org/3.5/library/pdb.html#pdbcommand-where
該模塊定義了以下功能;每個進入調試器的方式略有不同:
pdb.set_trace()
從正在運行的程序中插入調試器的典型用法是插入
import pdb; pdb.set_trace()
在要進入調試器的位置插入上面的函數。然后,你可以按照此語句逐步執行代碼,並使用continue命令在沒有調試器的情況下繼續運行。
大多數情況都只使用set_trace()函數和上面命令進行配合調試,下面的函數使用得比較少,沒能查找到過多下面函數使用的情況,如果有小伙伴有這方面的資料,希望可以告知,謝謝
-
pdb.
run
(statement, globals=None, locals=None) -
在調試器控制下執行語句(以字符串或代碼對象的形式給出)。調試器提示符出現在執行任何代碼之前;您可以設置斷點並鍵入continue來運行到斷點,或者使用step或next逐步執行語句。可選的全局變量和局部變量指定執行代碼的環境;默認情況下使用模塊__main__的字典。(參見內置的exec()或eval()函數的說明。)
-
pdb.
runeval
(expression, globals=None, locals=None) -
計算調試器控制器下的表達式(以字符串或代碼對象的形式給出)。當runeval()返回時,它返回表達式的值。否則,這個函數類似於run()。
-
pdb.
runcall
(function, *args, **kwds) -
使用給定的參數調用函數(函數或方法對象,而不是字符串)。當runcall()返回時,它返回函數調用返回的任何值。一旦輸入函數,調試器提示符就會出現。
-
pdb.
set_trace
() -
在調用堆棧幀中輸入調試器。這對於在程序中在給定點硬編碼斷點非常有用,即使代碼沒有被調試(例如,當斷言失敗時)。
-
pdb.
post_mortem
(traceback=None) -
輸入給定traceback對象的事后調試。如果沒有給出traceback,則使用當前正在處理的異常之一(如果要使用缺省值,則必須處理異常)。
-
pdb.
pm
() -
輸入在sys.last_traceback中找到的traceback的事后調試。
run*函數和set_trace()是實例化Pdb類和調用同名方法的別名。如果你想獲得更多的功能,你必須自己做:
-
class
pdb.
Pdb
(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False) -
Pdb是調試器類。
completekey、stdin和stdout參數被傳遞給底層cmd.Cmd
類;
如果給定了skip參數,則它必須是可迭代的全局樣式模塊名稱模式。調試器不會進入起源於與這些模式之一匹配的模塊中的框架。
默認情況下,當您發出continue命令時,Pdb為SIGINT信號設置一個處理程序(當用戶在控制台上按Ctrl-C時發送該信號)。這允許您通過按Ctrl-C再次進入調試器。如果希望Pdb不接觸SIGINT處理程序,請將nosigint設置為true。啟用跟蹤與skip的例子調用:
import pdb; pdb.Pdb(skip=['django.*']).set_trace()
新版本3.2:nosigint參數。以前,Pdb從未設置過SIGINT處理程序。