调试程序
如果自学速度慢、难点搞不定,欢迎来报 实战班 学习。
白月黑羽 1对1连线 讲解答疑,大量练习实战,学习效果是自学没法比的。还有商业项目实战,可以写入简历。
点击这里查看实战班介绍 咨询微信:18502556834
为什么要调试
我们开发的程序运行的时候,经常会发现运行的结果和我们预期的不符。
这就是程序运行的错误,我们通常叫做bug。
有两种类型的bug: 语句错误
和 逻辑错误
。
所谓 语句错误
,就是执行代码时,解释器就可以直接发现的代码错误,它没法继续执行,直接就会报错。
比如
出现这种问题,我们处理比较简单,去看报错提示,确定我们错误的代码在哪一行。
比如上面,解释器明确告诉我们,错误在 aa.py 文件的 14 行, 而且错误是: str类型的对象和字符串不能使用除号 /
有了这样的错误提示,我们就知道去检查对应代码,很快就可以发现:fee 是个字符串,不能和 headcount 这个整数 进行除法运算。
然后再改正一下,比如把代码变成这样
avgfee = int(fee) / headcount
但是还有的情况,解释器并不报错,只是运行的结果和我们预期的不一样。
这种就是 逻辑错误
。
我们来看一个例子:
某手机商店,在其官方网站上有API接口,给出每天 各品牌 手机销量,API请求地址是
http://cdn1.python3.vip/files/py/0016_price
访问这个请求地址,返回的内容如下所示。
其中逗号分隔的列,其含义依次是 : 手机型号,库存,当天销量,价格
三星S8, 110,50,5200
三星Note8, 123,30,6800
华为Mate10, 170,170,4100
华为P10, 167,57,3300
小米 6, 133,81,2200
小米 mix, 173,61,3200
现在我们要获取:最热卖的手机型号。
大家可以先自己想想怎么写代码。
我们来看下面的一段代码
import requests
# 用requests获取销量信息
res = requests.get('http://cdn1.python3.vip/files/py/0016_price')
content = res.text
# 下面两个变量记录当前找到的销量最多的手机和卖出数量
# 初始值都是None
mostsoldphone = None
mostsoldcount = None
for info in content.splitlines():
info = info.strip()
# 去掉空行
if not info:
continue
items = info.split(',')
# 销量在倒数第二列,获取销量信息
soldcount = items[-2]
# 型号在 第一列, 获取型号信息
phonetype = items[0]
# 如果前面已经有销量最多的手机记录,和当前这款手机销量比较
if mostsoldphone:
# 如果当前这款手机销量更高,把它置为最热卖手机
if mostsoldcount < soldcount:
mostsoldcount = soldcount
mostsoldphone = phonetype
# 如果前面没有有销量最多的手机记录,说明这是第一条记录
# 暂时先把它置为最热卖手机
else:
mostsoldcount = soldcount
mostsoldphone = phonetype
print(f'最热卖手机是 {mostsoldphone}, 销量是 {mostsoldcount}')
大家根据注释理解一下代码的逻辑。
咋一看应该没有问题吧?
好的,运行一下看看。发现结果如下
最热卖手机是 小米 6, 销量是 81
怎么回事?我们眼睛一看都知道,最热卖的是 华为Mate10 ,卖出了 170部。
怎么算成小米6了?
这时候,解释器并没有报错,说明没有任何Python语法语义上的错误,而是代码处理逻辑有问题。
怎么办?
很多初学者,往往这时候第一反应就是有点懵,不知道该怎么办。
然后,就是反复的看代码。 有时也不能发现代码到底错在哪里。
其实这时候,最有效的方法的就是去 调试程序
所谓调试程序就是:检查程序运行过程中的 一些关键步骤里面变量 ,看看是否正确。从而判断出是哪里代码的问题。
调试程序的方法
IDE的断点调试
最常用的方法就是 使用IDE的调试功能, 在关键代码处 设置断点, 查看关键变量的值。
什么是设置断点?
设置断点就是 设置某些代码行位置,当 程序运行 到这些位置,就会暂停执行。
我们在程序运行过程中, 查看某个变量的值,必须要让运行的程序能停在相应的位置。
下面我们就以Pycharm 为例,看一下。
首先看决定程序运行结果的关键变量和关键代码有哪些
上面这个程序大体分为2 步
-
从服务器获取销量信息
-
分析信息,获得最热销手机型号
首先第一步必须正确,也就是获取回来的销量信息是否和需求说的一致
所以我们可以在如下代码处设置断点
Pycharm要设置断点非常简单,在要设置断点的代码左边边框上,也就是上图的箭头指向的地方,点击一下鼠标就可以了。
设置好了后,就会出现上图所示的一个红点。
接下来,代码编辑界面,右键点击鼠标,选择debug 调试运行程序,如下图所示
注意不要选Run, Run是直接运行,不会停留在断点处
过一会儿就会发现,程序已debug模式运行了,并且会停留在刚才设置的断点处 。 如下所示
注意,高亮的一行,表示程序执行 暂停在此处,而且此处高亮行 代码 尚未执行
这时,我们想查看变量content的值(因为里面是获取的具体手机销售信息)。 由于当前暂停的一行还没有执行,所以content变量没有得到值,需要我们执行一行代码,把当前行执行结束,content变量才会被赋值。
那么怎样去执行一行代码呢?
IDE 通常有2种方式。
一种是step over, 对应下方圈起来的按钮
点击该按钮,IDE就会让当前的程序执行完当前行的代码。 如果该代码里面有函数调用,执行 不会暂停在函数里面 ,而是直接运行完所有的函数里面的代码, 暂停在 下一行代码。
另一种是step into, 对应下方圈起来的按钮
点击该按钮,IDE就会让当前的程序执行一步当前行代码。 如果该代码里面有函数调用,执行 就会暂停在函数里面 。
这里我们可以点击Step Over 的按钮,全部执行完当前行,光标就会停在下一行,同时下方的Variables窗口里面会显示变量content的值,如图所示
由于这个字符串比较长,我们可以点击箭头处的view, 就会弹出下面的信息框,显示出完整的变量内容
可以看出读出的内容没有错。
那么下一个要检查的关键的变量 在后面,获取每款手机销量,并进行判断的代码中。
所以,可以设置新的断点,如下图所示
然后点击下图所示按钮,表示继续调试程序,程序运行就会停在下一个断点处
这时候,如果我们仔细观察,要比较的两个变量 mostsoldcount 和 soldcount
细心的读者,就会发现这个两个变量的类型都是字符串。
对字符串进行大小的比较 ?? !!!
这就是问题所在:我们忘了把字符串转为整数类型了,导致比较销量大小的时候出现了问题。
这时,就可以结束调试。修改代码,加上转化为整数的操作,如下
soldcount = int(items[-2])
重新运行一下,发现结果如下
最热卖手机是 华为Mate10, 销量是 170
这下就正确了。
print出关键变量的值
如果我们运行程序的机器上没有安装IDE, 可以简单一些,直接用代码打印出关键变量的值。 检查是否符合我们的预期就可以了。
比如可以在上面的程序加上一些print语句
import requests
# 用requests获取销量信息
res = requests.get('http://cdn1.python3.vip/files/py/0016_price')
content = res.text
print(f'得到的字符串内容为:{content}')
# 下面两个变量记录当前找到的销量最多的手机和卖出数量
# 初识值都是None
mostsoldphone = None
mostsoldcount = None
for info in content.splitlines():
info = info.strip()
# 去掉空行
if not info:
continue
items = info.split(',')
# 销量在倒数第二列,获取销量信息
soldcount = items[-2]
# 型号在 第一列, 获取型号信息
phonetype = items[0]
# 如果前面已经有销量最多的手机记录,和当前这款手机销量比较
if mostsoldphone:
# 如果当前这款手机销量更高,把它置为最热卖手机
print(f'mostsoldcount为:{mostsoldcount}')
print(f'soldcount为:{soldcount}')
if mostsoldcount < soldcount:
print(f'程序判断 {mostsoldcount} < {soldcount}')
mostsoldcount = soldcount
mostsoldphone = phonetype
else:
print(f'程序判断 {mostsoldcount} > {soldcount}')
# 如果前面没有有销量最多的手机记录,说明这是第一条记录
# 暂时先把它置为最热卖手机
else:
mostsoldcount = soldcount
mostsoldphone = phonetype
print(f'最热卖手机是 {mostsoldphone}, 销量是 {mostsoldcount}')
运行结果如下
得到的字符串内容为:三星S8, 110,50,5200
三星Note8, 123,30,6800
华为Mate10, 170,170,4100
华为P10, 167,57,3300
小米 6, 133,81,2200
小米 mix, 173,61,3200
mostsoldcount为:50
soldcount为:30
程序判断 50 > 30
mostsoldcount为:50
soldcount为:170
程序判断 50 > 170
mostsoldcount为:50
soldcount为:57
程序判断 50 < 57
mostsoldcount为:57
soldcount为:81
程序判断 57 < 81
mostsoldcount为:81
soldcount为:61
程序判断 81 > 61
最热卖手机是 小米 6, 销量是 81
分析输出可以发现, 如下地方有明显异常
mostsoldcount为:50
soldcount为:170
程序判断 50 > 170
这时候可以,有些经验的程序员就会怀疑两个变量的类型是否是整数了,检查一下代码就可以发现原来是字符串,而不是整数。
使用哪种方法?
用 IDE 设置断点 和 添加打印语句 都可以调试程序。
那么我们该使用哪种方法呢?
通常在开发的时候,我们使用 IDE设置断点的方法, 因为毕竟比 添加打印语句 方便, 可以方便的查看任意变量 的值, 可以随意添加 断点。
但是有的时候,程序运行的环境没有 IDE, 比如 给用户使用了, 这时,出现问题,可以使用添加print 语句查看关键变量的方法进行调试。
有些软件程序,我们在开发的时候,就应该写代码 把一些关键信息 保存下来, 当然不是打印到屏幕,而是 写入日志文件中。 这样有些问题,有时候我们不需要修改代码,添加调试打印语句,直接看日志文件就能发现问题所在。