跳转至

调试程序

点击这里,边看视频讲解,边学习以下内容

为什么要调试

我们开发的程序运行的时候,经常会发现运行的结果和我们预期的不符。

这就是程序运行的错误,我们通常叫做bug。

有两种类型的bug: 语句错误逻辑错误


所谓 语句错误 ,就是执行代码时,解释器就可以直接发现的代码错误,它没法继续执行,直接就会报错。

比如

image

出现这种问题,我们处理比较简单,去看报错提示,确定我们错误的代码在哪一行。

比如上面,解释器明确告诉我们,错误在 aa.py 文件的 14 行, 而且错误是: str类型的对象和字符串不能使用除号 /

有了这样的错误提示,我们就知道去检查对应代码,很快就可以发现:fee 是个字符串,不能和 headcount 这个整数 进行除法运算。

然后再改正一下,比如把代码变成这样

avgfee = int(fee) / headcount


但是还有的情况,解释器并不报错,只是运行的结果和我们预期的不一样。

这种就是 逻辑错误

我们来看一个例子:

某手机商店,在其官方网站上有API接口,给出每天 各品牌 手机销量,API请求地址是

https://cdn2.byhy.net/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('https://cdn2.byhy.net/files/py/0016_price')
content = res.content.decode('utf8')

# 下面两个变量记录当前找到的销量最多的手机和卖出数量
# 初始值都是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 步

  1. 从服务器获取销量信息

  2. 分析信息,获得最热销手机型号

首先第一步必须正确,也就是获取回来的销量信息是否和需求说的一致

所以我们可以在如下代码处设置断点

image

Pycharm要设置断点非常简单,在要设置断点的代码左边边框上,也就是上图的箭头指向的地方,点击一下鼠标就可以了。

设置好了后,就会出现上图所示的一个红点。

接下来,代码编辑界面,右键点击鼠标,选择debug 调试运行程序,如下图所示

image

注意不要选Run, Run是直接运行,不会停留在断点处


过一会儿就会发现,程序已debug模式运行了,并且会停留在刚才设置的断点处 。 如下所示

image

注意,高亮的一行,表示程序执行 暂停在此处,而且此处高亮行 代码 尚未执行

这时,我们想查看变量content的值(因为里面是获取的具体手机销售信息)。 由于当前暂停的一行还没有执行,所以content变量没有得到值,需要我们执行一行代码,把当前行执行结束,content变量才会被赋值。

那么怎样去执行一行代码呢?

IDE 通常有2种方式。

一种是step over, 对应下方圈起来的按钮

image

点击该按钮,IDE就会让当前的程序执行完当前行的代码。 如果该代码里面有函数调用,执行 不会暂停在函数里面 ,而是直接运行完所有的函数里面的代码, 暂停在 下一行代码。



另一种是step into, 对应下方圈起来的按钮

image

点击该按钮,IDE就会让当前的程序执行一步当前行代码。 如果该代码里面有函数调用,执行 就会暂停在函数里面


这里我们可以点击Step Over 的按钮,全部执行完当前行,光标就会停在下一行,同时下方的Variables窗口里面会显示变量content的值,如图所示

image

由于这个字符串比较长,我们可以点击箭头处的view, 就会弹出下面的信息框,显示出完整的变量内容

image

可以看出读出的内容没有错。

那么下一个要检查的关键的变量 在后面,获取每款手机销量,并进行判断的代码中。

所以,可以设置新的断点,如下图所示

image

然后点击下图所示按钮,表示继续调试程序,程序运行就会停在下一个断点处

image


这时候,如果我们仔细观察,要比较的两个变量 mostsoldcount 和 soldcount

image

细心的读者,就会发现这个两个变量的类型都是字符串。

对字符串进行大小的比较 ?? !!!

这就是问题所在:我们忘了把字符串转为整数类型了,导致比较销量大小的时候出现了问题。

这时,就可以结束调试。修改代码,加上转化为整数的操作,如下

    soldcount = int(items[-2])

重新运行一下,发现结果如下

最热卖手机是 华为Mate10, 销量是 170

这下就正确了。


print出关键变量的值

如果我们运行程序的机器上没有安装IDE, 可以简单一些,直接用代码打印出关键变量的值。 检查是否符合我们的预期就可以了。

比如可以在上面的程序加上一些print语句

import requests

# 用requests获取销量信息
res = requests.get('https://cdn2.byhy.net/files/py/0016_price')
content = res.content.decode('utf8')
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 语句查看关键变量的方法进行调试。

有些软件程序,我们在开发的时候,就应该写代码 把一些关键信息 保存下来, 当然不是打印到屏幕,而是 写入日志文件中。 这样有些问题,有时候我们不需要修改代码,添加调试打印语句,直接看日志文件就能发现问题所在。

您需要高效学习,找工作? 点击咨询 报名实战班

点击查看学员就业情况

vscode

vscode 也是 经常被用做 Python 开发的 IDE。

相比 Pycharm, vscode 的优点是启动速度快。

点击这里观看视频讲解 vscode 下 Python 的开发和调试



关于调试配置文件 launch.json 中,Python调试的参数 ,可以点击这里参考

课后练习

去做练习