寫過C語言的同學(xué)們想必都很懷念(讀者:¿)gdb調(diào)試器,使用gdb可以隨意在程序運(yùn)行過程中暫停流程、查看變量。
很多時候,我們單純分析代碼流程和日志信息無法定位的問題,都得靠調(diào)試器來幫忙;可以說有了調(diào)試器,程序員才是代碼世界完整的上帝。
Python/ target=_blank class=infotextkey>Python當(dāng)然也不示弱,同樣存在這樣的巴別塔可以讓人升天
——不過阿醬必須承認(rèn)的是,現(xiàn)代IDE集成的圖形化調(diào)試功能已經(jīng)很好使了,一般情況下使用命令行工具的場景并不多。
但是也確實存在無法使用圖形化IDE的情況,因此對pdb工具略作了解還是很有必要的。畢竟誰也不知道可能被扔給一個什么樣的環(huán)境啊哈哈
pdb的使用
作為解釋型語言,Python調(diào)試工具的使用跟gdb畢竟還是有區(qū)別的。
比如Python的調(diào)試就不需要什么符號表之類的東西,說到底,最終Python虛擬機(jī)執(zhí)行的邏輯也是自帶符號的。
也正是由于Python的這種特殊性,所有pdb其實有兩種不太一樣的使用方式,即侵入式和非侵入式。
其實按字面意思就很容易理解在兩種方式的使用。類比一下腦機(jī)接口,也分為侵入式和非侵入式。侵入式就表示要將電極、芯片植入大腦皮層,“侵入”人體;而非侵入式則是在頭骨外收集腦電波進(jìn)行分析。
同樣地,侵入式pdb調(diào)用就是將調(diào)用pdb的代碼直接寫入Python腳本當(dāng)中;而非侵入式則是從命令行調(diào)用pdb,執(zhí)行相應(yīng)被調(diào)試腳本。
侵入式pdb
使用方式如下代碼所示,在代碼中途插入一行調(diào)用:
import pdb; # pdb.set_trace() a = "just"b = "do" pdb.set_trace() c = ['p', 'y', 't', 'h', 'o', 'n']print(a)
運(yùn)行腳本,會進(jìn)入這樣一個交互式界面:
D:D:00-GitHubpython-examplesxuanyuanyulong2020-11-04-python-pdb>python test_pdb_intrusive.py> d:00-githubpython-examplesxuanyuanyulong2020-11-04-python-pdbtest_pdb_intrusive.py(21)<module>()-> c = ['p', 'y', 't', 'h', 'o', 'n'](Pdb)00-GitHubpython-examplesxuanyuanyulong020-11-04-python-pdb>python test_pdb_intrusive.py> d:D:00-GitHubpython-examplesxuanyuanyulong2020-11-04-python-pdb>python test_pdb_intrusive.py> d:00-githubpython-examplesxuanyuanyulong2020-11-04-python-pdbtest_pdb_intrusive.py(21)<module>()-> c = ['p', 'y', 't', 'h', 'o', 'n'](Pdb)00-githubpython-examplesxuanyuanyulong020-11-04-python-pdbtest_pdb_intrusive.py(21)<module>()-> c = ['p', 'y', 't', 'h', 'o', 'n'](Pdb)
到這里已經(jīng)啟動了pdb,并且打印內(nèi)容中-> c = ['p', 'y', 't', 'h', 'o', 'n']行首的箭頭,表示當(dāng)前程序執(zhí)行流到了這一行代碼,如果繼續(xù)執(zhí)行,將首先執(zhí)行該行。
非侵入式pdb
非侵入式要xue微簡單一些,最大的好處是不需要改動代碼。
我們在控制臺執(zhí)行以下命令:
D:D:00-GitHubpython-examplesxuanyuanyulong2020-11-04-python-pdb>python -m pdb test_pdb_intrusive.py> d:00-githubpython-examplesxuanyuanyulong2020-11-04-python-pdbtest_pdb_intrusive.py(1)<module>()-> import pdb; # pdb.set_trace()(Pdb)00-GitHubpython-examplesxuanyuanyulong020-11-04-python-pdb>python -m pdb test_pdb_intrusive.py> d:D:00-GitHubpython-examplesxuanyuanyulong2020-11-04-python-pdb>python -m pdb test_pdb_intrusive.py> d:00-githubpython-examplesxuanyuanyulong2020-11-04-python-pdbtest_pdb_intrusive.py(1)<module>()-> import pdb; # pdb.set_trace()(Pdb)00-githubpython-examplesxuanyuanyulong020-11-04-python-pdbtest_pdb_intrusive.py(1)<module>()-> import pdb; # pdb.set_trace()(Pdb)
可以看到,通過這種方式進(jìn)入調(diào)試,程序執(zhí)行流停在了程序開頭。
通過分析進(jìn)入調(diào)試時代碼執(zhí)行流的位置,我們可以發(fā)現(xiàn),實際上侵入式的插入pdb.set_trace()調(diào)用,等價于我們從命令行啟動pdb,然后在這個調(diào)用的下一行打了一個斷點,然后直接運(yùn)行程序。
簡單命令
gdb中有一些常用的簡單命令,本節(jié)阿醬帶大家熟悉一下,后續(xù)會做更深入的討論。
h(elp)
在pdb界面下輸入h或help命令,即可列出pdb中支持的各種命令:
(Pdb) h Documented commands (type help <topic>):========================================EOF c d h list q rv undisplaya cl debug help ll quit s untalias clear disable ignore longlist r source untilargs commands display interact n restart step upb condition down j next return tbreak wbreak cont enable jump p retval u whatisbt continue exit l pp run unalias where Miscellaneous help topics:==========================exec pdb
在pdb后帶一個命令作為參數(shù),還可進(jìn)一步看到相應(yīng)的使用說明:
(Pdb) h hh(elp) Without argument, print the list of available commands. With a command name as argument, print help about that command. "help pdb" shows the full pdb documentation. "help exec" gives help on the ! command.
相信我,help其實才是pdb里面最重要的命令。別的什么都可以記不住,但是help一定要記住。在以結(jié)果為導(dǎo)向的職場生活中也是一樣,遇到問題要及時求助喲~
l(ist)
打印當(dāng)前文件的源代碼。不帶參數(shù)的話,默認(rèn)打印當(dāng)前行前后共計11行代碼。繼續(xù)執(zhí)行該命令的話,則會繼續(xù)往后打印最多11行代碼,直到遇上文件結(jié)束符EOF。
用.作為參數(shù)則限定要強(qiáng)一點,只會打印當(dāng)前行前后11行代碼。
(Pdb) l 1 -> import pdb; # pdb.set_trace() 2 3 4 def addStr(a, b): 5 return a + b 6 8 return ''.join(l) 9 10 def getSlogan(a, b, c): 11 result = addStr(a, b) + mergeChar(c)
當(dāng)指定兩個參數(shù)時,則打印這個區(qū)間內(nèi)的代碼:
(Pdb) l 3, 7 3 4 def addStr(a, b): 5 return a + b 6 7 -> def mergeChar(l: list):
而當(dāng)?shù)诙€參數(shù)b比第一個參數(shù)a小的時候,則表示“從第a行開始,繼續(xù)往后打印b行”,也就是總共打印(1+b)行:
(Pdb) l 7, 3 7 -> def mergeChar(l: list): 8 return ''.join(l) 9 10 def getSlogan(a, b, c):
p/pp
打印某個對象的值。區(qū)別在于pp調(diào)用的是pprint函數(shù),打印更加美觀。
(Pdb) p a'just'(Pdb) p addStr<function addStr at 0x000002087B0F9C80>