开源图书《Python完全自学教程》10.4调试

2022年7月26日 371点热度 0人点赞 0条评论

10.4 调试

在计算机程序中,调试(Debug)是发现和减少程序错误的过程。显然,这是一项实践性非常强的工作,不是看几页书就能完全掌握的,需要“久经考验”,不断总结经验,才能练就火眼金睛。但是,这也不意味着调试的技巧就无从学起。本节就根据经验介绍几种常用方法,供读者参考练习——当然是以 Python 程序为例,其他编程语言的程序会有所不同。

1. 使用 print() 函数

这是一种非常简单、直观的调试方法,也是在开发实践中应用最普遍的方法。使用 print() 函数,将代码中必要的内容输出到控制台,通过观察输出结果,判断代码可能存在的问题。例如:

#coding:utf-8
'''
filename: debugprint.py
'''

def mean(x, y):
    return x + y / 2

if __name__ == "__main__":
    a = input('input an integer:')
    b = input('input an other integer: ')
    aver = mean(a, b)
    print(f"{aver} is the average of {a} and {b}")

如果运行上述程序,会抛出异常。

% python debugprint.py
input an integer:2
input an other integer: 4
Traceback (most recent call last):
  File "/Users/qiwsir/Documents/my_books/codes/debugprint.py", line 11in <module>
    aver = mean(a, b)
  File "/Users/qiwsir/Documents/my_books/codes/debugprint.py", line 6in mean
    return x + y / 2
TypeError: unsupported operand type(s) for /: 'str' and 'int'

根据已学知识,“一眼”就知道异常出现的原因了。暂请佯装不知,以便能“演下去”。

从回溯(Traceback)中可知,最终出问题的在 line 6 中的 return x + y / 2 。因为这里就是用到了函数的两个参数,所以应该先看看传给函数 mean() 的两个参数的值是什么。于是可以在函数中增加 print() (注意,只增加打印输出,不对程序做任何其他修改)。

#coding:utf-8
'''
filename: debugprint.py
'''

def mean(x, y):
    print(f"{x}==>{type(x)}")
    print(f"{y}==>{type(y)}")
    return x + y / 2

if __name__ == "__main__":
    a = input('input an integer:')
    b = input('input an other integer: ')
    aver = mean(a, b)
    print(f"{aver} is the average of {a} and {b}")

执行后的结果为:

% python debugprint.py
input an integer:2
input an other integer: 4
2==><class 'str'>
4==><class 'str'>
Traceback (most recent call last):

  File "/Users/qiwsir/Documents/my_books/self-learning-python-codes/codes/debugprint.py", line 13in <module>
    aver = mean(a, b)
  File "/Users/qiwsir/Documents/my_books/self-learning-python-codes/codes/debugprint.py", line 8in mean
    return x + y / 2
TypeError: unsupported operand type(s) for /: 'str' and 'int'

从上述输出结果可知,传给 mean() 的两个实参,与输入的一样,其类型是字符串类型。这就找到了导致 TypeError: unsupported operand type(s) for /: 'str' and 'int' 异常的原因,也就能制定修改方案了。

当将输入变量 ab 分别转化为整数或浮点数之后,虽然没有上述异常信息了,但程序还有“逻辑错误”——请读者自行调试并修改。

2. 使用 pdb 模块

Python 标准库中的 pdb 模块是一个交互式的代码调试器,利用它能实现设置断点、单步执行等操作(官方文档:https://docs.python.org/zh-cn/3/library/pdb.html)。

在 Python 交互模式中,执行如下操作。

>>> import pdb
>>> def fun(x, y):
...     z = x + y
...     k = x * y
...     r = z / k
...     return r
...
>>> pdb.run("fun(1, 0)")      # (1)
> <string>(1)<module>()->None
(Pdb)

注释(1)用 pdb.run() 函数来执行前面定义的函数 fun() ——必须要给函数提供实参。写入注释(1)所示的代码之后,敲回车,出现后面的信息,光标停留在 (Pdb) 的后面,等待输入指令。输入 n (next),表示继续运行,出现如下所示结果:

>>> pdb.run("fun(1, 0)")       # (1)
> <string>(1)<module>()->None
(Pdb) n
ZeroDivisionError: division by zero
> <string>(1)<module>()->None
(Pdb)

说明函数中抛出了 ZeroDivisionError 异常。继续输入 n 直到调试结束,返回到 >>> 状态。

> <string>(1)<module>()->None
(Pdb) n
--Return--
> <string>(1)<module>()->None
(Pdb) n
Traceback (most recent call last):
  File "<stdin>", line 1in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/pdb.py", line 1597in run
    Pdb().run(statement, globals, locals)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/bdb.py", line 580in run
    exec(cmd, globals, locals)
  File "<string>", line 1in <module>
  File "<stdin>", line 4in fun
ZeroDivisionError: division by zero
>>>

如果用 pdb 模块调试程序,以前面编写的 debugprint.py 文件为例,在文件所在位置,执行下述指令,并进入到 (Pdb) 状态。

% python3 -m pdb debugprint.py
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(2)<module>()
-> '''
(Pdb) 

显示已经执行到 debugprint.py 文件的第二行的程序文档。而后等待输入后续调试指令。

输入 l ,显示当前所调试程序的所有代码(此处省略显示)。

输入 n ,进行单步执行。

(Pdb) n
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(5)<module>()
-> def mean(x, y):
(Pdb) 

这样就进入到函数 mean() 部分。如此,一步一步地调试下去。

> /Users/qiwsir/Documents/my_books/codes/debugprint.py(10)<module>()
-> if __name__ == "__main__":
(Pdb) n
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(11)<module>()
-> a = input('input an integer:')
(Pdb) n
input an integer:2
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(12)<module>()
-> b = input('input an other integer: ')
(Pdb) n
input an other integer: 4
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(13)<module>()
-> aver = mean(a, b)
(Pdb) n
2==><class 'str'>
4==><class 'str'>
TypeError: unsupported operand type(s) for /: 'str' and 'int'
> /Users/qiwsir/Documents/my_books/codes/debugprint.py(13)<module>()
-> aver = mean(a, b)
(Pdb) n
--Return--
> <string>(1)<module>()->None
(Pdb) 

直到发现异常。

(Pdb) 状态下输入 q 退回到 >>> Python 交互模式。

除了一步一步地调试,也可以在程序中使用 pdb 模块,例如下面的修改:

#coding:utf-8
'''
filename: debugprint.py
'''

import pdb
def mean(x, y):
    print(f"{x}==>{type(x)}")
    print(f"{y}==>{type(y)}")
    pdb.set_trace()            # (2) 到此则自动暂停
    return x + y / 2

if __name__ == "__main__":
    a = input('input an integer:')
    b = input('input an other integer: ')
    aver = mean(a, b)
    print(f"{aver} is the average of {a} and {b}")

执行此程序,到注释(2)处程序会自动暂停。

% python debugprint.py
input an integer:2
input an other integer: 4
2==><class 'str'>
4==><class 'str'>
> /Users/qiwsir/Documents/my_books/self-learning-python-codes/codes/debugprint.py(10)mean()
-> return x + y / 2
(Pdb) 

输入 p a 则会显示变量 a 的值,输入 p b 显示变量 b 的值。

(Pdb) p a
'2'
(Pdb) p b
'4'
(Pdb) n
TypeError: unsupported operand type(s) for /: 'str' and 'int'
> /Users/qiwsir/Documents/my_books/self-learning-python-codes/codes/debugprint.py(10)mean()
-> return x + y / 2

这样也定位到了问题之所在。

此处仅以简要的示例说明 pdb 模块在调试中的应用,更复杂的有关调试命令,请参阅官方文档。

3. 使用 IDE 的调试功能

目前常用的 IDE 都提供了非常友好的调试功能,恐怕这才是除了 print() 之外,开发者更常用的。下面就以 Visual Studio Code(简称 VS Code) 为例,说明调试方法(其他 IDE 也有调试功能,可以参阅有关专门资料)。

如果已经按照第1章1.8.3节所述,在 Visual Studio Code 上安装了 Python 扩展,其中就包含了调试功能。

如图10-4-1所示,单击左侧活动栏的 Run and Debug(运行和调试,如箭头(1)所示)按钮,进入到调试界面,点击箭头(2)所示的按钮或者按下快捷键 F5 ,即可运行当前的 Python 程序(仍然以 debugprint.py 为例)。

图片

图10-4-1 VS Code 的调试按钮

注意观察终端,会提示输入两个整数,依次输入之后,呈现图10-4-2所示的状态。

图片

图10-4-2 VS Code 调试效果

中间最显眼的地方,显示当前抛出异常的行和有关异常信息;左侧显示了有关调试信息;顶部还有几个按钮,能够实现单步执行。

“打断点”(Breakpoint)是调试程序的重要技能,在 VS Code 中,可以在任意逻辑行左侧点一下鼠标(如图10-4-3所示),则设置改行为断点,程序运行到此即暂停。

图片

图10-4-3 打断点

执行程序,当运行到图10-4-3所示的断点位置是,即显示图10-4-4所示的效果。

图片

图10-4-4 在断点处暂停

此后可以继续通过点击顶部的按钮向下执行。

在 VS Code 中,可以通过配置 launch.json 文件,制定更复杂的调试功能。对 VS Code 的应用介绍,已经超出了本书范畴,读者可以参考有关专门资料。总之,各种 IDE 提供了简单、直观调试功能,是调试程序的好工具。

自学建议

第1章1.8.3节的【自学建议】中不推荐读者把太多精力花在对 IDE 的选择和配置上,因为那是刚刚开始编程,注意力不能过于分散。时过境迁,现在已经敲过很多代码了,有了足够的编程经验,且对自己的计算机也比较熟悉了,非常有必要将生产代码的工具弄得先进一些。故特别建议读者应该花一些时间深入研究一番自己选定的 IDE ,配置好各种提升效率的功能,并应用于编写代码、调试程序的实践。

点击【阅读原文】,查阅更多开源图书《Python完全自学教程》的内容

图片

56840开源图书《Python完全自学教程》10.4调试

这个人很懒,什么都没留下

文章评论