世界人都知道,程序总会有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。省略若干网页……