世界人都知道,程序總會有bug存在。復雜點的bug一般人不能一眼看出,這就一要一套調試程序的手段。
方法一:使用print()函數直接打印:
>>> def foo(s): ... n = int(s) ... print(n) ... return 10 / n ... >>> def main(): ... foo('0') ... >>> main() 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in main File "<stdin>", line 4, in foo ZeroDivisionError: division by zero
我們在認為可能出錯的地方打印變量出來。這有很大的弊端,因為打印的代碼實際功能並不需要。這都是垃圾信息。
方法二:用斷言assert代替打印print()
>>> def foo(s): ... n = int(s) ... assert n != 0,'n的值是0!' ... return 10 / n ... >>> def main(): ... foo('0') ... >>> main() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in main File "<stdin>", line 3, in foo AssertionError: n的值是0!
assert的意思是,緊跟其后的表達式的結果應該是true,否則會拋出AssertionError。這里 n = 0,所以結果是AssertionError: n的值是0!
如果assert僅僅這樣的話,那和print區別也不大嘛。下面就是assert特點嘍:啟動python解釋器的時候可以用-O參數來關閉assert(這是大寫的字母O;關閉后,可以把assert的語句當做pass用),將上述代碼放進新建文件err_assert.py中,執行結果:
PS E:\Python3.6.3\workspace> python -O err_assert.py Traceback (most recent call last): File "err_assert.py", line 9, in <module> main() File "err_assert.py", line 7, in main foo('0') File "err_assert.py", line 4, in foo return 10 / n ZeroDivisionError: division by zer
方法三:用logging替換print(),和assert比,logging不會拋出錯誤,而是可以輸出到文件中
新建一個err_logginginfo.py文件:
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n=%d' % n)
print(10/n)
#執行結果
PS E:\Python3.6.3\workspace> python err_logginginfo.py INFO:root:n=0 Traceback (most recent call last): File "err_logginginfo.py", line 6, in <module> print(10/n) ZeroDivisionError: division by zero
logging可以允許你指定記錄信息的級別,級別由低到高分別有debug、info、warning、error、CRITICAL等級別,定義為高級別的時候,低級別的信息就不在顯示了。比如,我們調整為WARNING級別,看看INFO還有作用嗎:
import logging logging.basicConfig(level=logging.WARNING) s = '0' n = int(s) logging.info('n=%d' % n) print(10/n)
執行結果:
PS E:\Python3.6.3\workspace> python err_logginginfo.py Traceback (most recent call last): File "err_logginginfo.py", line 6, in <module> print(10/n) ZeroDivisionError: division by zero
這樣,我們可以放心輸出不同級別的信息,也不用刪除。統一的控制輸出哪個級別的信息。
上述例子中,logging都是把結果輸出到控制台console,logging可實現的作用非常豐富,例如,我們簡單配置后,還可以把結果輸出到文件,甚至通過tcp協議,將日志內容發送到網絡。
方法四:使用python的調試器pdb,可以讓程序以單步方式執行,方便我們隨時查看運行狀態。
新建程序err_pdb.py文件:
s = '0' n = int(s) print(10 / n)
然后以pdb模式啟動:
PS E:\Python3.6.3\workspace> python -m pdb err_pdb.py > e:\python3.6.3\workspace\err_pdb.py(1)<module>() -> s = '0' (Pdb) l 1 -> s = '0' 2 n = int(s) 3 print(10 / n) [EOF] (Pdb) n > e:\python3.6.3\workspace\err_pdb.py(2)<module>() -> n = int(s) (Pdb) p s '0' (Pdb) p n *** NameError: name 'n' is not defined (Pdb) n > e:\python3.6.3\workspace\err_pdb.py(3)<module>() -> print(10 / n) (Pdb) p n 0 (Pdb) p s '0' (Pdb) n ZeroDivisionError: division by zero > e:\python3.6.3\workspace\err_pdb.py(3)<module>() -> print(10 / n) (Pdb) n --Return-- > e:\python3.6.3\workspace\err_pdb.py(3)<module>()->None -> print(10 / n) (Pdb) n ZeroDivisionError: division by zero > <string>(1)<module>()->None (Pdb) n --Return-- > <string>(1)<module>()->None (Pdb) n Traceback (most recent call last): File "E:\Python3.6.3\lib\pdb.py", line 1667, in main pdb._runscript(mainpyfile) File "E:\Python3.6.3\lib\pdb.py", line 1548, in _runscript self.run(statement) File "E:\Python3.6.3\lib\bdb.py", line 431, in run exec(cmd, globals, locals) File "<string>", line 1, in <module> File "e:\python3.6.3\workspace\err_pdb.py", line 3, in <module> print(10 / n) ZeroDivisionError: division by zero Uncaught exception. Entering post mortem debugging Running 'cont' or 'step' will restart the program > e:\python3.6.3\workspace\err_pdb.py(3)<module>()->None -> print(10 / n) (Pdb) q Post mortem debugger finished. The err_pdb.py will be restarted > e:\python3.6.3\workspace\err_pdb.py(1)<module>() -> s = '0' (Pdb) n > e:\python3.6.3\workspace\err_pdb.py(2)<module>() -> n = int(s)
(Pdb) q
PS E:\Python3.6.3\workspace>
小寫字母l,可以列出所有要執行的代碼;
n 命令表示單步執行代碼;
p 后面加上變量名,可以隨時查看變量的值;
在pdb模式中,對於還沒有單步執行到的代碼,相關的變量的變更是無效的;
q 命令退出當前調試,進入重新從頭開始調試,再次輸入q,就會推出調試程序。
這種方式的調試,有一個弊端,就是只能一步一步的執行下去,如果程序有很多行,豈不是累死。
方法五:在可能出錯的地方使用pdb.set_trace(),就可以設置一個斷電:
#err_pdb.py import pdb s = '0' n = int(s) pdb.set_trace() #程序運行到這里會自動停止,等待命令 print(10 / n)
這時,我們可以使用l、c、n、p、q等命令來控制和查看程序:
PS E:\Python3.6.3\workspace> python err_pdb.py > e:\python3.6.3\workspace\err_pdb.py(7)<module>() -> print(10 / n) (Pdb) p s '0' (Pdb) l 2 import pdb 3 4 s = '0' 5 n = int(s) 6 pdb.set_trace() #程序運行到這里會自動停止,等待命令 7 -> print(10 / n) [EOF] (Pdb) n ZeroDivisionError: division by zero > e:\python3.6.3\workspace\err_pdb.py(7)<module>() -> print(10 / n) (Pdb) c Traceback (most recent call last): File "err_pdb.py", line 7, in <module> print(10 / n) ZeroDivisionError: division by zero
方法六:中級調試武器,IDE。省略若干網頁……