python错误调试print、assert、logging、pdb、pdb.set_trace()

世界人都知道,程序总会有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.3workspace> 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.3workspace> 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.3workspace> 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.3workspace> python -m pdb err_pdb.py
> e:python3.6.3workspaceerr_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.3workspaceerr_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.3workspaceerr_pdb.py(3)<module>()
-> print(10 / n)
(Pdb) p n
0
(Pdb) p s
'0'
(Pdb) n
ZeroDivisionError: division by zero
> e:python3.6.3workspaceerr_pdb.py(3)<module>()
-> print(10 / n)
(Pdb) n
--Return--
> e:python3.6.3workspaceerr_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.3libpdb.py", line 1667, in main
    pdb._runscript(mainpyfile)
  File "E:Python3.6.3libpdb.py", line 1548, in _runscript
    self.run(statement)
  File "E:Python3.6.3libdb.py", line 431, in run
    exec(cmd, globals, locals)
  File "<string>", line 1, in <module>
  File "e:python3.6.3workspaceerr_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.3workspaceerr_pdb.py(3)<module>()->None
-> print(10 / n)
(Pdb) q
Post mortem debugger finished. The err_pdb.py will be restarted
> e:python3.6.3workspaceerr_pdb.py(1)<module>()
-> s = '0'
(Pdb) n
> e:python3.6.3workspaceerr_pdb.py(2)<module>()
-> n = int(s)
(Pdb) q
PS E:Python3.6.3workspace>

小写字母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.3workspace> python err_pdb.py
> e:python3.6.3workspaceerr_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.3workspaceerr_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。省略若干网页……

原文地址:https://www.cnblogs.com/hiwuchong/p/8583869.html