真叫人頭禿!Python也有pdb

文 |?軒轅御龍
來源:Python 技術(shù)「ID: pythonall」

C有g(shù)db,Python也有pdb
寫過C語言的同學(xué)們想必都很懷念(讀者:?)gdb調(diào)試器,使用gdb可以隨意在程序運(yùn)行過程中暫停流程、查看變量。
很多時(shí)候,我們單純分析代碼流程和日志信息無法定位的問題,都得靠調(diào)試器來幫忙;可以說有了調(diào)試器,程序員才是代碼世界完整的上帝。
Python當(dāng)然也不示弱,同樣存在這樣的巴別塔可以讓人升天

——不過阿醬必須承認(rèn)的是,現(xiàn)代IDE集成的圖形化調(diào)試功能已經(jīng)很好使了,一般情況下使用命令行工具的場景并不多。
但是也確實(shí)存在無法使用圖形化IDE的情況,因此對pdb工具略作了解還是很有必要的。畢竟誰也不知道可能被扔給一個(gè)什么樣的環(huán)境啊哈哈
pdb的使用
作為解釋型語言,Python調(diào)試工具的使用跟gdb畢竟還是有區(qū)別的。
比如Python的調(diào)試就不需要什么符號表之類的東西,說到底,最終Python虛擬機(jī)執(zhí)行的邏輯也是自帶符號的。
也正是由于Python的這種特殊性,所有pdb其實(shí)有兩種不太一樣的使用方式,即侵入式和非侵入式。
其實(shí)按字面意思就很容易理解在兩種方式的使用。類比一下腦機(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)入這樣一個(gè)交互式界面:
D:\000-GitHub\python-examples\xuanyuanyulong\2020-11-04-python-pdb>python?test_pdb_intrusive.py
>?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py(21)()
->?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:\000-GitHub\python-examples\xuanyuanyulong\2020-11-04-python-pdb>python?-m?pdb?test_pdb_intrusive.py
>?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py(1)()
->?import?pdb;?#?pdb.set_trace()
(Pdb)可以看到,通過這種方式進(jìn)入調(diào)試,程序執(zhí)行流停在了程序開頭。
通過分析進(jìn)入調(diào)試時(shí)代碼執(zhí)行流的位置,我們可以發(fā)現(xiàn),實(shí)際上侵入式的插入
pdb.set_trace()調(diào)用,等價(jià)于我們從命令行啟動pdb,然后在這個(gè)調(diào)用的下一行打了一個(gè)斷點(diǎn),然后直接運(yùn)行程序。
簡單命令
gdb中有一些常用的簡單命令,本節(jié)阿醬帶大家熟悉一下,后續(xù)會做更深入的討論。
h(elp)
在pdb界面下輸入
h或help命令,即可列出pdb中支持的各種命令:(Pdb)?h
Documented?commands?(type?help?):
========================================
EOF????c??????????d????????h?????????list??????q????????rv???????undisplay
a??????cl?????????debug????help??????ll????????quit?????s????????unt
alias??clear??????disable??ignore????longlist??r????????source???until
args???commands???display??interact??n?????????restart??step?????up
b??????condition??down?????j?????????next??????return???tbreak???w
break??cont???????enable???jump??????p?????????retval???u????????whatis
bt?????continue???exit?????l?????????pp????????run??????unalias??where
Miscellaneous?help?topics:
==========================
exec??pdb在pdb后帶一個(gè)命令作為參數(shù),還可進(jìn)一步看到相應(yīng)的使用說明:
(Pdb)?h?h
h(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其實(shí)才是pdb里面最重要的命令。別的什么都可以記不住,但是help一定要記住。在以結(jié)果為導(dǎo)向的職場生活中也是一樣,遇到問題要及時(shí)求助喲~l(ist)
打印當(dāng)前文件的源代碼。不帶參數(shù)的話,默認(rèn)打印當(dāng)前行前后共計(jì)11行代碼。繼續(xù)執(zhí)行該命令的話,則會繼續(xù)往后打印最多11行代碼,直到遇上文件結(jié)束符EOF。
用
.作為參數(shù)則限定要強(qiáng)一點(diǎn),只會打印當(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)指定兩個(gè)參數(shù)時(shí),則打印這個(gè)區(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ù)诙€(gè)參數(shù)b比第一個(gè)參數(shù)a小的時(shí)候,則表示“從第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
打印某個(gè)對象的值。區(qū)別在于
pp調(diào)用的是pprint函數(shù),打印更加美觀。(Pdb)?p?a
'just'
(Pdb)?p?addStr!
使用
!可以在pdb環(huán)境下,執(zhí)行一般的Python語句。通常我們可以用來改變變量的值——要不怎么說調(diào)試器可以讓你成為上帝呢?還有比這更為所欲為的嗎?一般的話這個(gè)
!其實(shí)可以省略,但是當(dāng)要執(zhí)行語句開頭的單詞與pdb的已有命令沖突,就得不到預(yù)期結(jié)果了,所以建議還是加上。這個(gè)用
!領(lǐng)起命令的做法跟vim編輯器的邏輯很像,可以類比記憶。不熟悉的讀者可以忽略。(Pdb)?!a?=?"python"
(Pdb)?p?a
'python'r(eturn)
pdb中,
r和return表示同一個(gè)意思,即“運(yùn)行當(dāng)前函數(shù)直到返回”。這一點(diǎn)上,
r在pdb和gdb中的含義是不同的。讀者不必在意run/restart
表示重新運(yùn)行當(dāng)前被調(diào)試程序。使用這個(gè)命令,可以為需要傳入?yún)?shù)的腳本傳入所需參數(shù)。
格式與命令行執(zhí)行該腳本一樣,只是把相應(yīng)的python命令和腳本路徑替換為了
run或restart。(Pdb)?run?a?b?c?d?kkk
Restarting?test_pdb_intrusive.py?with?arguments:
????????test_pdb_intrusive.py
>?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py(1)()
->?import?pdb;?#?pdb.set_trace()
(Pdb)?!import?sys
(Pdb)?p?sys.argv
['test_pdb_intrusive.py',?'a',?'b',?'c',?'d',?'kkk']b(reak)
查看/添加斷點(diǎn)。
不帶任何參數(shù)時(shí),即列出當(dāng)前已有斷點(diǎn)。
(Pdb)?b?21
Breakpoint?1?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
(Pdb)?b?17
Breakpoint?2?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
(Pdb)?b
Num?Type?????????Disp?Enb???Where
1???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
2???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17同時(shí),通過
run和restart重新運(yùn)行被調(diào)試程序,不會清除已有斷點(diǎn):(Pdb)?run
Restarting?test_pdb_intrusive.py?with?arguments:
????????test_pdb_intrusive.py
>?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py(1)()
->?import?pdb;?#?pdb.set_trace()
(Pdb)?b
Num?Type?????????Disp?Enb???Where
1???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
2???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17cl(ear)
有三種使用方式:1)類似設(shè)置斷點(diǎn)時(shí),清除特定文件特定行的斷點(diǎn);2)將要清除的斷點(diǎn)號列出來,以空格分隔;3)不帶參數(shù),清除所有斷點(diǎn)。
下面一一演示:
1)類似設(shè)置斷點(diǎn)時(shí),清除特定文件特定行的斷點(diǎn)
(Pdb)?b
Num?Type?????????Disp?Enb???Where
1???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
2???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
(Pdb)?clear?test_pdb_intrusive.py:21
(Pdb)?b
Num?Type?????????Disp?Enb???Where
2???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:172)將要清除的斷點(diǎn)號列出來,以空格分隔
(Pdb)?b?21
Breakpoint?3?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
(Pdb)?b?15
Breakpoint?4?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?b
Num?Type?????????Disp?Enb???Where
2???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
3???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
4???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?clear?2?4
Deleted?breakpoint?2?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
Deleted?breakpoint?4?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?b
Num?Type?????????Disp?Enb???Where
3???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:213)不帶參數(shù),清除所有斷點(diǎn)
(Pdb)?b?17
Breakpoint?5?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
(Pdb)?b?15
Breakpoint?6?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?b
Num?Type?????????Disp?Enb???Where
3???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
5???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
6???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?b
Num?Type?????????Disp?Enb???Where
3???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
5???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
6???breakpoint???keep?yes???at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?clear
Clear?all?breaks??yes
Deleted?breakpoint?3?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:21
Deleted?breakpoint?5?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:17
Deleted?breakpoint?6?at?d:\000-github\python-examples\xuanyuanyulong\2020-11-04-python-pdb\test_pdb_intrusive.py:15
(Pdb)?b
好了打住打住,寫之前感覺pdb沒多少東西,沒想到寫起來才發(fā)現(xiàn),這么一點(diǎn)內(nèi)容就已經(jīng)這么多了,今天又熬夜了……

狗命要緊各位讀者老爺后會有期、后會有期
總結(jié)
pdb的內(nèi)容出乎意料地豐富,還有很多內(nèi)容在這篇文章中都沒能涉及。
軟件調(diào)試其實(shí)也是一門很有趣的學(xué)問,當(dāng)然,也是一門很有用的學(xué)問

由于微信平臺算法改版,公號內(nèi)容將不再以時(shí)間排序展示,如果大家想第一時(shí)間看到我們的推送,強(qiáng)烈建議星標(biāo)我們和給我們多點(diǎn)點(diǎn)【在看】。星標(biāo)具體步驟為:
(1)點(diǎn)擊頁面最上方“小詹學(xué)Python”,進(jìn)入公眾號主頁。
(2)點(diǎn)擊右上角的小點(diǎn)點(diǎn),在彈出頁面點(diǎn)擊“設(shè)為星標(biāo)”,就可以啦。
感謝支持,比心。
