2.6 Python调试
调试是Python编程中非常重要的一环。程序出现什么问题,查看抛出的异常。或者处处加print和log找出错误点,再慢慢地反推,是可以找到问题、解决问题的,但是有更简单的方法为什么非得舍易取难呢?
在Linux和Windows平台有很多第三方调试工具,一般的Python IDE基本也自带了调试工具。工具太多了反而不好选择,而且也不是随手就能找到第三方调试工具的。这里仅示范手头上必定有的Python自带的调试工具,其他的第三方调试工具都大同小异,熟悉了最简单的,其他的也就无师自通了。
2.6.1 Windows下IDLE调试
先写一个简单的程序来做示例。既然是调试,最好的选择莫过于多次调用函数的阶乘了,这个程序简单又明显,适合用来做示例。打开IDLE,单击菜单栏的File|New File,创建一个新文档,编辑代码,如图2-27所示。
图2-27 winDebugFactorial.py
单击菜单栏File|Save As,选择保存位置后将文件保存为winDebugFactorial.py。下面开始调试winDebugFactorial.py。
单击IDLE菜单栏的Run|Python Shell,打开Python Shell,如图2-28所示。
图2-28 打开Python Shell
单击Python Shell菜单栏的Debug|Debugger,打开Debug Control窗口,如图2-29所示。
图2-29 打开Debug Control窗口
然后在IDLE窗口为代码添加断点。所谓断点,简单地说就是调试程序时需要停顿的位置,一般在函数的入口、参数变化的行添加。这里只在fac函数入口添加一个断点。单击fac函数入口行,再右击,弹出的快捷菜单中选择Set Breakpoint,如图2-30所示。
图2-30 设置断点
现在可以开始运行调试程序了,单击IDLE窗口菜单栏中的Run|Run Module,如图2-31所示。
图2-31 运行调试程序
单击Debug Control窗口的Go按钮,开始运行程序,然后单击Debug Control窗口的Step按钮,逐步运行程序。如果需跳出循环或者跳出函数,则单击Debug Control窗口的Out按钮。Debug Control窗口中的Stack检查框显示的是程序当前运行位置,Locals检查框显示的是当前变量的值,如图2-32所示。
图2-32 Debug Control
通过Debug调试很容易发现程序中的错误之处。虽然这个Debug工具比较简陋,但基本功能都还齐全,算是比较好用的一款Debug工具了。
2.6.2 Linux下pdb调试
Linux下的Python调试工具也很多,最简单、最方便的可能就是pdb了。pdb功能齐全,使用方便,命令几乎是一模一样的。先写一个示范程序,用pdb调试一下。
【示例2-16】打开Putty连接到Linux,执行命令:
cd code/crawler vi linuxBugListExtremum.py
linuxBugListExtremum.py的代码如下:
1 #!/usr/bin/env python3 2 #-*- coding: utf-8 -*- 3 __author__ = 'hstking hst_king@hotmail.com' 4 5 import cls 6 import time 7 8 def getList(): 9 #构建一个纯数字列表 10 numList = [] 11 num = 'q' 12 while num: 13 cls.clear() 14 print(numList) 15 print('结束构建列表,请按回车') 16 num = input('请输入一个整数:') 17 if num == '': 18 break 19 try: 20 num = int(num) 21 except ValueError: 22 print('要求输入整数,请重新输入') 23 time.sleep(1) 24 continue 25 numList.append(num) 26 return numList 27 28 def getMaxNum(List): 29 #获取列表中最大值 30 #import pdb 31 #pdb.set_trace() 32 num = List[0] 33 for i in List[1:]: 34 if num <= i: 35 num = i 36 return num 37 38 def getMinNum(List): 39 #获取列表中最小值 40 num = List[0] 41 for i in List[1:]: 42 if num >= i: 43 num = i 44 return num 45 46 47 if __name__ == '__main__': 48 numList = getList() 49 maxNum = getMaxNum(numList) 50 print('列表中最大值为:%d' %maxNum) 51 minNum = getMinNum(numList) 52 print('列表中最小值为:%d' %minNum)
linuxBugListExtremum.py程序让用户输入一组整数放入列表中,然后从列表中挑选出最大值和最小值。以linuxBugListExtremum.py为例,使用pdb调试。
第5行的import cls导入的是一个自定义模块cls.py。代码如下:
1 #!/usr/bin/env python3 2 #-*- coding: utf-8 -*- 3 __author__ = 'hstking hst_king@hotmail.com' 4 5 import platform 6 import os 7 8 def clear(): 9 OS = platform.system() 10 if OS == 'Windows': 11 os.system('cls') 12 else: 13 os.system('clear') 14 15 16 17 if __name__ == '__main__': 18 pass
下面先简单地介绍一下pdb。pdb在Python中是以模块的形式出现的,它是Python的标准库,可以在Python交互环境中使用,如图2-33所示。
图2-33 模块式使用pdb
也可以在程序中间插入一段程序,相当于在一般IDE里面打上断点,然后启动debug,不过这种方式是hardcode的,如图2-34所示。
图2-34 程序内使用pdb
将pdb放入程序内,在运行程序时,运行到pdb行后就暂停了,然后开始运行pdb程序。这种方式需要改动程序,比较麻烦。
笔者更喜欢最后一种方法,即用命令行启动目标程序,加上-m参数调用pdb模块,如图2-35所示。
图2-35 命令调用pdb模块
图2-35显示了pdb的所有命令,这里只说明最常用的几个:
- list:显示程序,可以带参数。比如显示第5行list 5。
- break:添加断点。比如在第5行添加断点break 5,在getList函数添加断点break。
- run:开始运行程序。
- step:单步运行,进入函数内部。
- next:单步运行,不进入函数内部。
- print:显示参数。
- quit:退出pdb。
下面开始调试linuxBugListExtremum.py程序。执行命令:
python -m pdb linuxBugListExtremum.py list 52 break getList break getMaxNum break getMinNum break
执行结果如图2-36所示。
图2-36 pdb加入断点
执行命令run,开始运行程序,函数外的行使用next单步运行,到了函数入口后使用step单步运行,中途使用print命令随时监视变量变化,如图2-37所示。
图2-37 调试linuxBugListExtremum.py
调试完毕后输入quit,退出pdb。pdb没有GUI,用起来似乎没有那么直观,习惯了还挺方便。如果偏爱GUI,那还是找一个Python IDE吧,Eclipse + pydev就很方便,支持多个操作平台,除了块头大一点,没有什么缺点;或者找一个短小精干的Atom(vscode),也非常方便。
提示
pdb是Python调试工具,也是Python的标准模块之一,所以也可以用import将其导入程序中使用。在Windows中也可以使用pdb。