程序能一次寫完並正常運行的機率很小,基本不超過1%。總會有各類各樣的bug須要修正。有的bug很簡單,看看錯誤信息就知道,有的bug很複雜,咱們須要知道出錯時,哪些變量的值是正確的,哪些變量的值是錯誤的,所以,須要一整套調試程序的手段來修復bug。python
第一種方法簡單直接粗暴有效,就是用print()
把可能有問題的變量打印出來看看:eclipse
err3.py插件
def foo(s): n=int(s) print('>>>n=%d' % n) return 10 / n def main(): foo('0') main()
運行結果,在輸出中能夠查找到打印的變量值命令行
>>>n=0 Traceback (most recent call last): File "err3.py", line 7, in <module> main() File "err3.py", line 6, in main foo('0') File "err3.py", line 4, in foo return 10 / n ZeroDivisionError: division by zero
用print()最大的壞處是程序調試完畢須要刪除,程序處處都是print(),運行結果也包含大量垃圾信息。因此,咱們又有第二種方法debug
do_assert.py調試
斷言code
def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / n def main(): foo('0') main()
assert的意思是,表達式n!=0應該是True,不然,根據程序運行的邏輯,後面的代碼確定會出錯blog
若是斷言失敗,assert語句自己就會拋出AssertionErrorip
運行結果get
Traceback (most recent call last): File "do_assert.py", line 12, in <module> main() File "do_assert.py", line 10, in main foo('0') File "do_assert.py", line 6, in foo assert n != 0, 'n is zero!' AssertionError: n is zero!
程序中若是處處充斥着assert
,和print()
相比也好不到哪去。不過,啓動Python解釋器時能夠用-O
參數來關閉assert
:
python3 -O do_assert.py
運行結果
Traceback (most recent call last): File "do_assert.py", line 12, in <module> main() File "do_assert.py", line 10, in main foo('0') File "do_assert.py", line 7, in foo return 10 / n ZeroDivisionError: division by zero
關閉後,能夠把全部的assert語句當成pass來看
logging
把print()替換成logging是第3種方式,和assert比,logging不會拋出錯誤,並且能夠輸出到文件
err4.py
import logging #logging.basicConfig(level=logging.INFO) s='0' n=int(s) logging.info('n=%d'%n) print(10/n)
logging.info()就能夠輸出一段文本。運行,發現除了ZeroDivisionError,沒有任何信息
Traceback (most recent call last): File "err4.py", line 6, in <module> print(10/n) ZeroDivisionError: division by zero
修改增長配置
import logging logging.basicConfig(level=logging.INFO) s='0' n=int(s) logging.info('n=%d'%n) print(10/n)
運行結果
INFO:root:n=0 Traceback (most recent call last): File "err4.py", line 6, in <module> print(10/n) ZeroDivisionError: division by zero
這就是logging
的好處,它容許你指定記錄信息的級別,有debug
,info
,warning
,error
等幾個級別,當咱們指定level=INFO
時,logging.debug
就不起做用了。同理,指定level=WARNING
後,debug
和info
就不起做用了。這樣一來,你能夠放心地輸出不一樣級別的信息,也不用刪除,最後統一控制輸出哪一個級別的信息。
logging
的另外一個好處是經過簡單的配置,一條語句能夠同時輸出到不一樣的地方,好比console和文件。
pdb
第4種方式是啓動python的調速器pdb,讓程序以單步方式運行,能夠隨時查看運行狀態
err5.py
#err5.py s='0' n=int(s) print(10 / n)
啓動
python3 -m pdb err5.py
以參數-m pdb啓動後,pdb定位到下一步要執行的代碼->s='0'
> /nas/scripts/python/learn-python3/samples/debug/err5.py(2)<module>() -> s='0'
能夠輸入字母l來查看代碼
(Pdb) l 1 #err5.py 2 -> s='0' 3 n=int(s) 4 print(10 / n)
輸入n能夠單步執行代碼
(Pdb) n > /nas/scripts/python/learn-python3/samples/debug/err5.py(3)<module>() -> n=int(s)
> /nas/scripts/python/learn-python3/samples/debug/err5.py(4)<module>() -> print(10 / n)
(Pdb) n ZeroDivisionError: division by zero > /nas/scripts/python/learn-python3/samples/debug/err5.py(4)<module>() -> print(10 / n)
任什麼時候候均可以輸入p 加變量名查看變量
(Pdb) p s '0'
輸入q結束調試
這種經過pdb在命令行調試的方法理論上是萬能的,但實在是太麻煩了,若是有一千行代碼,要運行到第999行得敲多少命令啊。還好,咱們還有另外一種調試方法。
pdb.set_trace()
這個方法也是用pdb,可是不須要單步執行,咱們只須要import pdb
,而後,在可能出錯的地方放一個pdb.set_trace()
,就能夠設置一個斷點:
err6.py
#err6.py import pdb s='0' n=int(s) #運行到這來會自動暫停 pdb.set_trace() print(10 / n)
運行代碼,程序會自動在pdb.set_trace()
暫停並進入pdb調試環境,能夠用命令p
查看變量,或者用命令c
繼續運行:
(base) [root@prd-zabbix debug]# python3 err6.py > /nas/scripts/python/learn-python3/samples/debug/err6.py(7)<module>() -> print(10 / n) (Pdb) p n 0 (Pdb) c Traceback (most recent call last): File "err6.py", line 7, in <module> print(10 / n) ZeroDivisionError: division by zero
IDE
若是要比較爽地設置斷點、單步執行,就須要一個支持調試功能的IDE。目前比較好的Python IDE有:
Visual Studio Code:https://code.visualstudio.com/,須要安裝Python插件。
PyCharm:http://www.jetbrains.com/pycharm/
另外,Eclipse加上pydev插件也能夠調試Python程序。
小結
寫程序最痛苦的事情莫過於調試,程序每每會以你意想不到的流程來運行,你期待執行的語句其實根本沒有執行,這時候,就須要調試了。
雖然用IDE調試起來比較方便,可是最後你會發現,logging纔是終極武器。