Skip to content

调试

Debug 调试是编程人员的一项重要技能。只有当你学会 debug 了以后,才可以知道程序的正确的流程。

debug 介绍

在正式讲解之前,先来了解下 debug 这个词的由来,就像我们初学 Python 时,先要了解下它历史的由来。

1937年,美国青年霍华德·艾肯找到 IBM 公司为其投资 200 万美元研制计算机,第一台成品艾肯把它取名为:马克 1 号(mark1),又叫“自动序列受控计算机”,从这时起 IBM 公司由生产制表机,肉铺磅秤,咖啡研磨机等乱七八糟玩意儿行业,正式跨进“计算机”领地。 为马克 1 号编制程序的是哈佛的一位女数学家葛丽斯·莫雷·霍波,有一天,她在调试程序时出现故障,拆开继电器后,发现有只飞蛾被夹扁在触点中间,从而“卡”住了机器的运行。 于是,霍波诙谐的把程序故障统称为“臭虫(BUG)”,把排除程序故障叫 DEBUG ,而这奇怪的“称呼”,后来成为计算机领域的专业行话。从而 debug 意为排除程序故障的意思。 ——百度百科

看了上面的小故事,debug 一词的由来,是由 bug 词得来的,bug 是臭虫的意思,debug 就是解决臭虫。

在如今的互联网时代,多少你肯定听说过 bug 这个词,比如什么什么软件又出 bug 了!说的就是软件在使用的过程中,程序出现了一些错误。故称之为 bug。

而 debug 则是通过工具来对代码进行调试,一步步找出程序中出现 bug 的位置,也就是程序中具体错误代码的位置。就像故事中所说,debug 过程就是在解决虫子一样。。

启动 debug 模式

还是用示例说话,我们书写一段简短的代码,来帮我们完成今天要讲的内容。

pycharm 导航栏处,有个 run,点开以后即可看到 debug 。

创建并运行了 Car 脚本

python
class Car:

    def __init__(self, speed=0):
        self.speed = speed
        self.odometer = 0
        self.time = 0

    def say_state(self):
        print("里程 {}, 目前时速{}".format(self.odometer, self.speed))

    def accelerate(self):
        self.speed += 5

    def brake(self):
        self.speed -= 5

    def step(self):
        self.odometer += self.speed
        self.time += 1
        print('小车往前开动了一段距离')

    def average_speed(self):
        return self.odometer / self.time

    def main(self):
        while True:
            action = input("我该做什么? [A]加速, [B]刹车, "
                           "[O]显示里程表, [S]显示平均速度?").upper()
            if action not in "ABOS" or len(action) != 1:
                print("我不知道该怎么做")
                continue
            if action == 'A':
                self.accelerate()
            elif action == 'B':
                self.brake()
            elif action == 'O':
                print("车开了 {} 公里".format(self.odometer))
            elif action == 'S':
                print("汽车的平均速度为 {} 公里/小时".format(self.average_speed()))
            self.step()
            self.say_state()


if __name__ == '__main__':
    my_car = Car()
    print("我是一辆车!")
    my_car.main()

让我们看看当我们启动脚本时会发生什么,并且试着找出我们的平均速度:

image-20191227162810152

Pycharm 报告一个运行时错误: ZeroDivisionError。

让我们更深入地研究一下我们的代码,找出出了什么问题。 我们可以使用 PyCharm 调试器来查看代码中到底发生了什么。 要开始调试,必须首先设置一些断点。 要创建断点,只需单击 gutter 即可

image-20191227162922821

接下来,单击主子句旁边 Run icon 中的图标,然后选择 Debug‘。 Pycharm 启动调试会话并显示“调试工具”窗口。

image-20191227163028949

image-20191227163220072

点击按钮继续脚本执行,在 Console 选项卡中输入 s:

image-20191227163359343

单击 Run icon 按钮恢复脚本执行。 现在你瞧! 这里是个例外。 另一个断点也出现了: 默认情况下,PyCharm 会暂停代码中未捕获的任何异常,并显示一个带有闪电的断点图标。

image-20191227164952933

调试器还显示错误消息。 所以我们找到了问题所在。 在调试器中还可以看到 self.time 的值为零

修改错误

为了避免再次遇到同样的问题,我们添加一个 if 语句来检查时间是否等于零。 为此,在方法的平均速度中选择语句 return self.odometer / self.time,然后按 Ctrl + Alt + t (Code | round with) :

image-20191227164050447

PyCharm创建弹出一个提示框,我们可以选择给选中的代码添加上去。

详细调试

Debug 工具窗口显示框架(frames)、变量(variables)和监视器(watches)的专用窗格,以及显示所有输入和输出信息的控制台(console)。 如果希望控制台始终可见,可以将其拖动到 PyCharm 窗口的边缘。

单步调试

如果你希望逐行查看代码的内容,则不需要在每一行上设置断点,您可以逐步查看代码。

让我们来看看单步执行示例程序是什么样的: 单击 Resume 图标按钮,进入控制台以询问汽车的平均速度(类型为“ s”) ,我们可以看到我们按下了断点。

我们可以使用单步工具栏按钮来选择下一个要停止的行。

Stepping toolbar

例如,单击“ 跳过” 按钮,跳过图标然后看到蓝色标记移至下一行代码:

如果单击“单**步执行”**按钮进入图标,您将在该行之后看到

python
action = input("我该做什么? [A]加速, [B]刹车, "
               "[O]显示里程表, [S]显示平均速度?").upper()

调试器进入文件 parse.py

image-20191227165914603

但是,如果继续使用跳过图标,则会看到您的应用程序仅传递到下一个循环:

image-20191227170032321

如果您想专注于自己的代码,请使用进入我的代码”按钮 进入我的代码 -这样您就可以避免进入库类。

有关 详细信息,请参见步进工具栏 和单 步执行程序部分。

监视器

PyCharm允许您观看任何变量。只需单击添加按钮该工具栏上的手表 选项卡,然后输入你想要观看的变量的名称-my_car.time。请注意,此处提供代码完成功能:

image-20191228143804270

首先,您会看到时间等于 0 这意味着该变量尚未定义:

image-20191228143918659

但是,当程序执行继续到定义变量的范围时,监视将获得以下视图:

image-20191228144221688

有关详细信息,请参见添加,编辑和删除监视器 部分。

内联调试

您可能已经注意到了另一个PyCharm功能,它可以很容易地查看您的代码在做什么: 内联调试器 。按下任何断点后,PyCharm就会在编辑器中立即向您显示许多变量的值:

image-20191229220343024

计算表达式

最后,您可以随时评估任何表达式。例如,如果要查看变量的值,请单击评估表达式按钮 按钮。

然后在打开的对话框中,点击Evaluate

例如,如果输入里程表的期望值(例如50),然后继续单步执行脚本,则会得到以下信息:

image-20191229220748513

有关详细信息,请参见“ 评估表达式”部分。

快捷键

step over(F8快捷键):在单步执行时,在函数内遇到子函数时不会进入子函数内单步执行,而是将子函数整个执行完再停止,也就是把子函数整个作为一步。在不存在子函数的情况下是和step into效果一样的。简单的说就是,程序代码越过子函数,但子函数会执行,且不进入。

step into(F7快捷键):在单步执行时,遇到子函数就进入并且继续单步执行,有的会跳到源代码里面去执行。

step into my code(Alt+Shift+F7快捷键):在单步执行时,遇到子函数就进入并且继续单步执行,不会进入到源码中。

step out(Shift+F8快捷键):假如进入了一个函数体中,你看了两行代码,不想看了,跳出当前函数体内,返回到调用此函数的地方,即使用此功能即可。

Resume program(F9快捷键):继续恢复程序,直接运行到下一断点处。

以上四个功能,就是最常用的功能,一般操作步骤就是,设置好断点,debug运行,然后 F8 单步调试,遇到想进入的函数 F7 进去,想出来在 shift + F8,跳过不想看的地方,直接设置下一个断点,然后 F9 过去。