Python3之調試

  程序能一次寫完並正常運行的機率很小,基本不超過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的好處,它容許你指定記錄信息的級別,有debuginfowarningerror等幾個級別,當咱們指定level=INFO時,logging.debug就不起做用了。同理,指定level=WARNING後,debuginfo就不起做用了。這樣一來,你能夠放心地輸出不一樣級別的信息,也不用刪除,最後統一控制輸出哪一個級別的信息。

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纔是終極武器。

相關文章
相關標籤/搜索