調試是開發過程當中不可避免的一個環節,在Python中咱們使用print、logging、assert等方法進行調試既簡單又實用,但畢竟有其侷限性。今天這篇文章爲你們帶來三個工具,其中有Python的內置模塊也有第三方庫,它們提供了調試代碼所需的大部分經常使用功能,將極大的提高咱們的開發和bug排除效率。python
1.PDBgit
pdb是Python中的一個內置模塊,啓用pdb後能夠對代碼進行斷點設置和跟蹤調試。爲了演示方便,咱們準備一個樣例程序pdb_test.py:github
def countnumber(number): for i in range(number): print(i) if __name__ == '__main__': countnumber(10)
以後在終端中輸入python -m pdb pdb_test.py命令,進入pdb的調試模式:
這時咱們就能夠經過各類命令控制代碼執行或者查看當前變量,例如l能夠查看全部代碼,n是執行下一步代碼,p能夠查看當前變量等等,須要注意的是命令n只會執行主程序中的代碼,若是想要單步執行子函數中的代碼,須要使用s指令,調試效果以下:
這時咱們就能夠經過各類命令控制代碼執行或者查看當前變量,例如l能夠查看全部代碼,n是執行下一步代碼,p能夠查看當前變量等等,須要注意的是命令n只會執行主程序中的代碼,若是想要單步執行子函數中的代碼,須要使用s指令,調試效果以下:
能夠看到,經過s指令(若是隻想在主函數中單步執行可使用n)和p指令,咱們控制程序單步運行並實時查看了相關變量。可是單步執行畢竟是一種效率很是低下的調試方式,尤爲當代碼量比較大的時候更是噩夢,這時就須要用到pdb的set_trace()方法,咱們對樣例程序pdb_test.py作一點修改:django
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴, 互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' import pdb def countnumber(number): for i in range(number): print(i) pdb.set_trace() if __name__ == '__main__': countnumber(10)
pdb.set_trace()的做用就是在代碼中設置斷點,在pdb調試模式下,使用c命令就會直接跳轉到下一個斷點位置,若是以後沒有其餘斷點就會執行徹底部代碼,調試效果以下:
除了上面提到的幾個指令之外,pdb還有其餘一些比較經常使用的命令(見下表),綜合使用基本可以知足平常的調試需求。
2.Better-exceptionswindows
better-exceptions是一個Python第三方庫,做者對他的定義是「使異常信息更加美觀和詳盡」。在正式使用以前先說下這個庫的安裝:多線程
第一步,使用pip install better_exceptions安裝better-exceptions庫;
第二步,使用export BETTER_EXCEPTIONS=1(Linux / OSX)或setx BETTER_EXCEPTIONS 1(Windows)設置環境變量。app
如今就能夠正常使用better-exceptions進行調試了,爲了演示效果更加明顯,咱們對上文中的代碼稍做修改做爲本次的樣例程序better_test.py:dom
def divisionnumber(number, div): for i in range(div): print(number / i) if __name__ == '__main__': divisionnumber(10, 10)
很明顯,上面這段代碼在執行過程當中會由於分母爲0而拋出異常,如今咱們執行python better_test.py,看看啓用了better-exceptions後的異常信息是什麼樣子的:
從上面這幅圖能夠看出better-exceptions對異常信息的修改主要體如今兩個方面:ide
一是對產生異常的代碼進行了顏色標註;函數
二是對產生異常的代碼中的相關變量值進行了輸出(包括函數等對象);
這樣一來,不少時候咱們只須要根據better-exceptions輸出的輔助信息就能判斷產生異常的位置和緣由,而沒必要像之前同樣再次查看源代碼並觀察運行結果,正如做者所說:Pretty and more helpful。
可是,過多的信息輸出也會有問題,那就是當代碼層級結構比較複雜的時候,better-exceptions輸出的輔助信息可能會很是之多,就好比上面的divisionnumber函數,他所在的地址信息多數時候咱們並不關心,爲了屏蔽這些「垃圾」信息,咱們能夠在代碼中加一行:
better_exceptions.MAX_LENGTH = XXX
XXX是容許顯示的最大字符長度,好比這裏設置爲10,再來運行better_test.py這個程序就會是下面的結果:
能夠看到,對函數divisionnumber的註釋只顯示了最開始的"<function"這幾個字符。
除了上面提到的功能以外,better-exceptions還能夠和logging還有django無縫接入,這使得它的應用更加靈活,關於這方面內容你們能夠查看項目文檔。
還有一點須要提醒你們,若是你是在windows下使用,可能會出現下圖中的亂碼問題,這是因爲better-exceptions的內設編碼格式所致使的。
解決的辦法是在安裝後,對better_exceptions目錄下的encoding.py文件第10行代碼進行以下修改:
# 原代碼: ENCODING = locale.getpreferredencoding() # 修改成: ENCODING = 'utf-8'
3.PySnooper
PySnooper也是一個Python的第三方庫,他的特色是可以精準的顯示每條代碼的執行順序、執行時間以及隨之帶來的局部變量的改變等等。值得一提的是,做爲一個發佈不滿半年的庫,PySnooper在github上已經達到了1.2W星,其受歡迎程度可見一斑。
PySnooper的使用能夠說是很是的方便,直接在代碼中以裝飾器的形式調用就能夠了。固然在引用前你得使用pip install pysnooper或者conda install -c conda-forge pysnooper安裝這個庫。咱們仍是舉一個例子來進行演示,樣例代碼以下:
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴, 互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' import pysnooper import random @pysnooper.snoop() def foo(): lst = [] for i in range(10): lst.append(random.randrange(1, 1000)) lower = min(lst) upper = max(lst) mid = (lower + upper) / 2 print(lower, mid, upper) foo()
在上面這段代碼中,咱們先是生成10個1到1000之間的隨機數,而後計算他們之中的最大最小值和中位數,惟一的不一樣在於第三行多了一條語句@pysnooper.snoop(),咱們運行如下代碼,發現除了正常的print結果以外,多了許多內容(內容太多,下面只顯示一部分):
19:51:57.704857 call 16 def foo(): 19:51:57.705860 line 17 lst = [] New var:....... lst = [] 19:51:57.705860 line 18 for i in range(10): New var:....... i = 0 19:51:57.705860 line 19 lst.append(random.randrange(1, 1000)) Modified var:.. lst = [758] 19:51:57.705860 line 18 for i in range(10): Modified var:.. i = 1 .................... 19:51:57.706818 line 22 upper = max(lst) New var:....... upper = 927 19:51:57.706818 line 23 mid = (lower + upper) / 2 New var:....... mid = 552.0 19:51:57.706818 line 24 print(lower, mid, upper) 19:51:57.706818 return 24 print(lower, mid, upper) Return value:.. None
這都是PySnooper跟蹤監控的結果,正如上面所說,他準確記錄的每條代碼的運行時間、順序以及相關的變量值。
做爲一個星標1.2W+的項目,PySnooper的功能確定不會這麼簡單,@pysnooper.snoop()中是能夠接收參數的,好比咱們以爲輸出內容太多,能夠考慮把信息記錄到log日誌中,這個功能只須要加一個log文件定位參數就能搞定:
@pysnooper.snoop('file.log')
@pysnooper.snoop()支持的參數還有不少,分別對應了不一樣的功能,例如監控自定義表達式、監控底層函數、支持多線程等等,詳見項目文檔。
此外,pysnooper還支持局部監控,通常來講咱們寫的代碼都比較長,而須要監控的只是其中的一小部分,這時候就能夠把須要監控的代碼放到一個block裏。咱們修改下剛纔的代碼,只對計算最大最小值和中位數的部分進行監控,修改後的代碼以下:
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴, 互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' import pysnooper import random def foo(): lst = [] for i in range(10): lst.append(random.randrange(1, 1000)) with pysnooper.snoop(): lower = min(lst) upper = max(lst) mid = (lower + upper) / 2 print(lower, mid, upper) foo()
運行以後發現監控信息精簡了不少:
New var:....... lst = [562, 341, 552, 353, 628, 302, 430, 188, 955, 108] New var:....... i = 9 20:02:47.359272 line 21 lower = min(lst) New var:....... lower = 108 20:02:47.359272 line 22 upper = max(lst) New var:....... upper = 955 20:02:47.360269 line 23 mid = (lower + upper) / 2
使用with pysnooper.snoop()模式依然保留了對各類參數的支持,我的認爲這種模式更加符合實踐需求。
小結:今天介紹了三個不借助IDE就能方便使用的調試工具,三個工具的調試思路和適用場景也各不相同,你們能夠根據須要靈活選用。不過話說回來,我我的最喜歡的仍是PySnooper,你最喜歡哪一款呢?