做一手房用什么網(wǎng)站百度競價開戶需要多少錢
一、問題概述
回調(diào)函數(shù)是指一個函數(shù)執(zhí)行完后,調(diào)用另外一個函數(shù)的過程。
一般步驟是,回調(diào)函數(shù)作為參數(shù)傳遞給原始函數(shù),原始函數(shù)執(zhí)行完自己的邏輯后,自動調(diào)用回調(diào)函數(shù)并將自己的執(zhí)行結(jié)果作為參數(shù)傳遞給回調(diào)函數(shù)。
根據(jù)不同的用法,回調(diào)函數(shù)可能在主線程/進程中,也可能在其他線程/進程中。有時候,這會給調(diào)試回調(diào)函數(shù)帶來一點麻煩,比如,在回調(diào)函數(shù)內(nèi)打的斷點,在調(diào)試模式下死活無法觸發(fā)。(是的,在pycharm中遇到這個情況的就是我。。。)
這時,為了能正常調(diào)試,可以考慮以下方法:
1、如果回調(diào)函數(shù)是普通函數(shù),或者普通的類,可以先不作為回調(diào)函數(shù)使用,而是先當(dāng)成普通函數(shù)來正常調(diào)試,確認其中的邏輯沒問題, 再按回調(diào)函數(shù)的用法來調(diào)用。
2、不使用 pycharm 中內(nèi)置的調(diào)試功能,使用 ipdb(window) 或者 pudb(linux)來調(diào)試。
第1點比較簡單,以下介紹一下第 2 點中如何使用 ipdb 來調(diào)試回調(diào)函數(shù)。
二、ipdb 調(diào)試回調(diào)函數(shù)
1、ipdb是什么
pdb(python debugger)是一個集成于Python標(biāo)準(zhǔn)庫中的交互式無界面調(diào)試工具,功能主要包括:
a、斷點設(shè)置與跳轉(zhuǎn)
b、單步執(zhí)行代碼
c、任意變量查詢、值修改(不必重啟程序)
pdb 的弱點在于對多線程,遠程調(diào)試等支持得不夠好,沒有界面,不太適合大型的 python 項目。
ipdb是增強版的pdb,它提供了更多的功能和更友好的交互界面,使得在開發(fā)過程中調(diào)試代碼變得更加方便。 功能包括:
a、在代碼中的任意位置設(shè)置斷點
b、單步運行語句,并查看其結(jié)果
c、可查看當(dāng)前執(zhí)行上下文中的變量值
d、可跳過某個函數(shù)或循環(huán)
e、命令行界面
f、語法tab補全、條件斷點、彩色輸出等
2、ipdb的安裝與常用命令簡介
2.1、安裝
pip install ipdb
2.2、常用命令簡介
! 執(zhí)行 python 命令,或者顯示變量值
ENTER 重復(fù)上次命令
a(rgs) 打印當(dāng)前函數(shù)的參數(shù)
b(reakpoint) 設(shè)置斷點
cl(ear) 清除斷點
c(ontinue) 運行直到斷點位置
h(elp) 幫助信息
j(ump) 讓程序跳轉(zhuǎn)到指定的行數(shù)
l(ist) 列出想了解的代碼,查找當(dāng)前位置
n(ext) 讓程序運行下一行,當(dāng)前語句有函數(shù)/子程序調(diào)用也不進入
s(tep) 讓程序運行下一行,當(dāng)前語句有函數(shù)/子程序就進入
p(rint) 打印某個變量
q(uit) 退出調(diào)試
r(eturn) 繼續(xù)執(zhí)行,直到函數(shù)體返回
disable/enable 禁用/啟用斷點
3、ipdb的啟動
假設(shè) d:\download\callback_debug.py 需要調(diào)試。有兩種方式能夠?qū)λM行調(diào)試:
3.1、在IDE中啟動
a、在代碼中導(dǎo)入 ipdb 的 set_trace
b、在要調(diào)試的行之前插入 set_trace()
c、運行代碼,則代碼自動在 set_trace() 處停止,等待調(diào)試
3.2、在命令行啟動
python -m ipdb d:/download/callback_debug.py
方法 3.1 在IDE中執(zhí)行,會修改源碼,并且對循環(huán)等的調(diào)試支持不是很好,相對來說,方法 3.2 更加靈活一些,但是對多線程、多進程的場景,按方法 3.2,非主進程、主線程上的斷點不一定能抓住。
以下主要討論方法 3.2,但是使用方法也適用于 3.1。
4、舉例:以一個調(diào)試過程為例
4.0、原始代碼
def on_callback(num, num2):a = 1b = '這是一串字符'if num == 3:print('haha')print(f"回調(diào)處理后的數(shù)字是 {num} * {num} = {num * num}")def foo(num, callback: callable):print(f"當(dāng)前的數(shù)字是:{num}")callback(num, 10)if __name__ == '__main__':for i in range(5):foo(i, on_callback)
4.1、命令行啟動 ipdb
(base) D:\xxxx\trial>python -m ipdb callback_debug.py
# 執(zhí)行結(jié)果為:
> d:\xxxx\trial\callback_debug.py(1)<module>()
----> 1 def on_callback(num):2 if num == 3:3 print('haha')ipdb>
此時自動進入單步執(zhí)行狀態(tài),ipdb 停留在第一行代碼處待命。
4.2、查看代碼
查看第1行至第10行的代碼內(nèi)容(沒有第0行)
ipdb> l 1,10
----> 1 def on_callback(num):2 if num == 3:3 print('haha')4 print(f"回調(diào)處理后的數(shù)字是 {num} * {num} = {num * num}")5 6 7 def foo(num, callback: callable):8 print(f"當(dāng)前的數(shù)字是:{num}")9 callback(num)10 ipdb>
ll 是查看整個源碼文件
4.3、設(shè)置斷點
在 on_callback() 函數(shù)內(nèi)設(shè)置斷點,位置是第4、11行,分別在回調(diào)函數(shù)、初始函數(shù)內(nèi)。
ipdb> b 4
Breakpoint 1 at d:\xxxx\trial\callback_debug.py:4
ipdb> b 11
Breakpoint 2 at d:\xxxx\trial\callback_debug.py:11
ipdb>
4.4、運行至斷點處
斷點2后設(shè)置,但是運行邏輯上先運行到。
ipdb> c
當(dāng)前的數(shù)字是:0
> d:\xxxx\trial\callback_debug.py(11)foo()10 print(f"當(dāng)前的數(shù)字是:{num}")
2--> 11 callback(num, 10)12 ipdb>
4.5、在函數(shù) foo 里,查看都有哪些參數(shù)
ipdb> a
num = 0
callback = <function on_callback at 0x0000010B5C856940>
ipdb>
4.6、進入回調(diào)函數(shù)內(nèi)部
ipdb> s
--Call--
> d:\xxxx\trial\callback_debug.py(1)on_callback()
----> 1 def on_callback(num, num2):2 a = 13 b = '這是一串字符'ipdb>
4.7、連續(xù)單步運行
其中第一次單步執(zhí)行需要輸入 n 后再回車,之后2次直接回車即可
ipdb> n
> d:\xxxx\trial\callback_debug.py(2)on_callback()1 def on_callback(num, num2):
----> 2 a = 13 b = '這是一串字符'ipdb>
> d:\xxxx\trial\callback_debug.py(3)on_callback()2 a = 1
----> 3 b = '這是一串字符'
1 4 if num == 3:ipdb>
> d:\xxxx\trial\callback_debug.py(4)on_callback()3 b = '這是一串字符'
1---> 4 if num == 3:5 print('haha')ipdb>
4.8、跳出、跳入回調(diào)函數(shù),并查看入?yún)⑴c變量值
ipdb> u
> d:\xxxx\trial\callback_debug.py(11)foo()10 print(f"當(dāng)前的數(shù)字是:{num}")
2--> 11 callback(num, 10)12 ipdb> d
> d:\xxxx\trial\callback_debug.py(4)on_callback()3 b = '這是一串字符'
1---> 4 if num == 3:5 print('haha')ipdb> args
num = 0
num2 = 10
ipdb>p num
2
ipdb> num
2
ipdb>
4.9、跳轉(zhuǎn)到指定行(必須與跳轉(zhuǎn)之前位置在同一個函數(shù)內(nèi))
> d:\xxxx\trial\callback_debug.py(2)on_callback()1 def on_callback(num, num2):
----> 2 a = 13 b = '這是一串字符'ipdb> j 4
> d:\xxxx\trial\callback_debug.py(4)on_callback()3 b = '這是一串字符'
1---> 4 if num == 3:5 print('haha')ipdb>
4.10、取消第一個斷點并執(zhí)行代碼至第2個斷點處
ipdb> cl 1
Deleted breakpoint 1 at d:\xxxx\trial\callback_debug.py:4
ipdb> c
當(dāng)前的數(shù)字是:2
> d:\xxxx\trial\callback_debug.py(11)foo()10 print(f"當(dāng)前的數(shù)字是:{num}")
2--> 11 callback(num, 10)12 ipdb>
4.11、顯示當(dāng)前所處的位置以及堆棧信息
ipdb> wd:\anaconda3\lib\bdb.py(580)run()579 try:
--> 580 exec(cmd, globals, locals)581 except BdbQuit:<string>(1)<module>()d:\xxxx\trial\callback_debug.py(16)<module>()14 if __name__ == '__main__':15 for i in range(5):
---> 16 foo(i, on_callback)> d:\xxxx\trial\callback_debug.py(11)foo()10 print(f"當(dāng)前的數(shù)字是:{num}")
2--> 11 callback(num, 10)12ipdb>
4.12、退出調(diào)試
ipdb> q(base) D:\xxxx\trial>
三、參考資料
1、pdb調(diào)試神器使用終極指南
2、pytorch Debug —交互式調(diào)試工具Pdb (ipdb是增強版的pdb)-1-使用說明