每天学习一点Python——手把手教你用VSCode调试Python代码
大家好!今天我们来学习Python编程中非常重要的一项技能——调试。调试就是找bug、修bug的过程。无论你是编程新手还是有经验的开发者,调试都是必备技能!
一、准备工作
首先确保你安装了:
二、第一个调试示例
让我们从一个简单的例子开始:
# debug_demo.py
def calculate_sum(numbers):
total = 0
for num in numbers:
total += num
print(f"当前数字: {num}, 累计总和: {total}")
return total
# 测试数据
data = [1, 2, 3, 4, 5]
result = calculate_sum(data)
print(f"最终结果: {result}")
代码解析:
- • 第1行:定义了一个函数
calculate_sum,它接收一个参数numbers(一个数字列表) - • 第2行:初始化变量
total为0,用来存储总和 - • 第3-5行:
for循环遍历numbers中的每个数字 - • 第4行:
total += num 等价于 total = total + num,把当前数字加到总和中 - • 第8行:定义测试数据
data,是一个包含5个数字的列表 - • 第9行:调用函数,把测试参数
data传进去,计算结果
执行结果:
当前数字: 1, 累计总和: 1
当前数字: 2, 累计总和: 3
当前数字: 3, 累计总和: 6
当前数字: 4, 累计总和: 10
当前数字: 5, 累计总和: 15
最终结果: 15
三、如何在VSCode中开启调试?
方法一:使用调试面板
- 1. 点击VSCode左侧活动栏的"运行和调试"图标(看起来像一个甲壳虫趴在三角形上)
- 2. 如果是第一次使用,会提示你"创建 launch.json 文件",点击它
- 4. 这时会在调试框顶部中间位置出现一个绿色的开始调试按钮 ▶,点击它就开始调试了
方法二:快速设置断点
def debug_example():
name = "Python"
version = 3.9
print(f"语言: {name}, 版本: {version}")
# 设置断点的方法:点击行号左边的空白区域
for i in range(3):
print(f"循环 {i + 1}")
return "调试完成"
# 按F5开始调试
debug_example()
设置断点的步骤:
- 1. 找到第6行(
for i in range(3):)
四、调试界面详解
当你开始调试时,VSCode界面会分成几个区域:
+------------------------------------------+
| 调试控制台 | 变量查看器 | 监视窗口 |
+------------------------------------------+
| 编辑器 |
| (这里有你的代码,当前执行行会高亮显示) |
+------------------------------------------+
| 调用堆栈 |
| (显示当前函数调用关系) |
+------------------------------------------+
1. 调试控制台(Debug Console)
这里显示程序的输出结果,你还可以在这里输入Python命令,实时查看变量值。
2. 变量查看器(Variables)
这个窗口显示当前作用域的所有变量及其值。比如:
- • 当程序执行到第6行时,这里会显示:
name: "Python"
version: 3.9
i: 0 (循环计数器)
3. 监视窗口(Watch)
这里可以添加你想特别关注的表达式。比如添加i * 2,就能实时看到这个表达式的值。
五、调试控制按钮
调试窗口顶部有一排按钮,让我们来认识它们:
[▶ 继续] [↷ 单步跳过] [↓ 单步进入] [↑ 单步跳出] [↻ 重新开始] [■ 停止]
对比学习:
| | |
|---|
| | |
| | 查看自己写的calculate_average()函数 |
| | |
六、断点的高级用法
1. 条件断点
有时候我们只想在特定条件下暂停程序。比如:
def process_data(items):
for index, item in enumerate(items):
if item > 10: # 我们只想在item大于10时暂停
print(f"发现大数: {item} at index {index}")
else:
print(f"小数: {item}")
data = [5, 12, 8, 15, 3]
process_data(data)
设置条件断点的方法:
- 1. 在第3行
if item > 10:的左侧设置普通断点
2. 日志点(Logpoint)
有时候我们不想暂停程序,只想在特定位置输出信息:
def calculate_stats(numbers):
total = sum(numbers)
count = len(numbers)
average = total / count
return total, average
data = [85, 90, 78, 92, 88]
result = calculate_stats(data)
设置日志点:
- 1. 在第2行的下一行的行号左侧右键(因为如果直接在第二行添加,会报错:name 'total' is not defined) → 选择"添加记录点"
- 3. 程序执行到这里时会在 调试控制台 输出日志"计算总和: 433",但程序本身不会暂停
- 4. 注意:直接点击程序执行按钮不会执行该记录点,需要按F5来触发调试模式。
七、变量查看技巧
让我们看一个更复杂的例子:
def analyze_students():
students = {
"小明": {"年龄": 18, "成绩": 85},
"小红": {"年龄": 17, "成绩": 92},
"小刚": {"年龄": 19, "成绩": 78}
}
total_score = 0
student_count = 0
for name, info in students.items():
total_score += info["成绩"]
student_count += 1
current_avg = total_score / student_count
print(f"学生: {name}, 成绩: {info['成绩']}, 当前平均分: {current_avg:.1f}")
return total_score, current_avg
# 调试时观察变量变化
result = analyze_students()
print(f"最终结果: 总分={result[0]}, 平均分={result[1]:.1f}")
调试时你可以:
- 2. 在调试控制台直接计算:
>>> total_score * 2
510
>>> [name for name in students.keys()]
['小明', '小红', '小刚']
- 3. 添加监视表达式:在Watch窗口添加
current_avg > 85,实时查看条件是否成立
八、调试配置详解
VSCode的调试配置文件是launch.json,它控制着调试的行为:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: 调试当前文件",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"stopOnEntry":false,
"justMyCode":true,
"args": []
}
]
}
参数解释:
- •
"name": 调试配置的名称,显示在调试下拉菜单中 - •
"type": 调试器类型,Python就写"python" - •
"program": "${file}"表示调试当前打开的文件 - •
"console": "integratedTerminal"表示在VSCode的内置终端中运行 - •
"stopOnEntry": false表示开始时不暂停 - •
"justMyCode": true表示只调试你自己的代码,不进入Python内置函数 - •
"args": 命令行参数,比如可以设置["--input", "data.txt"]
九、unittest测试
单元测试是保证代码质量的重要手段,VSCode可以很好地调试测试代码:
# test_debug.py
import unittest
class TestMathOperations(unittest.TestCase):
def test_addition(self):
result = 1 + 2 # 在这行添加断点
self.assertEqual(result, 3)
print(f"加法测试通过: 1 + 2 = {result}")
def test_division(self):
# 设置断点调试异常情况
with self.assertRaises(ZeroDivisionError):
result = 5 / 0
def test_list_operations(self):
items = [1, 2, 3, 4, 5]
# 使用调试器观察列表变化
squared = [x**2 for x in items]
print(f"原始列表: {items}")
print(f"平方后: {squared}")
self.assertEqual(squared, [1, 4, 9, 16, 25])
if __name__ == '__main__':
unittest.main()
逐行解释
第1行:导入unittest模块
import unittest
- •
unittest 是Python自带的单元测试框架
第3-4行:定义测试类
class TestMathOperations(unittest.TestCase):
- • 创建一个测试类
TestMathOperations - • 必须继承
unittest.TestCase,这是测试类的标准写法
第5-8行:第一个测试方法 - 加法测试
def test_addition(self): # 方法名必须以test_开头,才会被unittest发现
result = 1 + 2 # 执行计算:1 + 2 = 3
self.assertEqual(result, 3) # 断言:验证result是否等于3
print(f"加法测试通过: 1 + 2 = {result}") # 输出信息
- • 方法名:必须以
test_ 开头(unittest的约定) - •
self.assertEqual(a, b):断言a等于b,如果不相等则测试失败
第10-14行:第二个测试方法 - 除法测试(验证异常)
def test_division(self):
# 设置断点调试异常情况
with self.assertRaises(ZeroDivisionError): # 验证是否会抛出ZeroDivisionError
result = 5 / 0 # 这行代码会抛出除零异常
- •
self.assertRaises(异常类型):验证代码块是否会抛出指定类型的异常 - •
5 / 0:一定会抛出 ZeroDivisionError - • 这个测试期望的行为是:
5 / 0 抛出异常,且被 assertRaises 捕获,测试通过
第16-23行:第三个测试方法 - 列表操作测试
def test_list_operations(self):
items = [1, 2, 3, 4, 5] # 创建列表
# 使用调试器观察列表变化
squared = [x**2 for x in items] # 列表推导式:计算每个元素的平方
print(f"原始列表: {items}") # 打印原始列表
print(f"平方后: {squared}") # 打印平方后的列表
self.assertEqual(squared, [1, 4, 9, 16, 25]) # 断言平方结果是否正确
第25-26行:主程序入口
if __name__ == '__main__':
unittest.main() # 运行所有测试
- •
unittest.main():自动发现并运行所有以 test_ 开头的方法
如何运行和调试:
方法1:直接运行测试
# 在终端运行
python test_debug.py
输出结果:
加法测试通过: 1 + 2 = 3
..原始列表: [1, 2, 3, 4, 5]
平方后: [1, 4, 9, 16, 25]
.
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
输出结果解析:
- •
.:每个点代表一个测试用例通过。这里有3个点,表示3个测试都通过了 - •
加法测试通过: 1 + 2 = 3:这是test_addition方法中的print输出 - •
原始列表: [1, 2, 3, 4, 5] 和 平方后: [1, 4, 9, 16, 25]:这是test_list_operations方法中的print输出 - •
Ran 3 tests in 0.001s:表示运行了3个测试用例,耗时0.001秒
方法2:调试测试用例(推荐)
步骤1:设置断点
- • 第19行:
squared = [x**2 for x in items]
步骤2:启动调试(按F5)
步骤3:使用测试专用调试配置(修改 .vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: 调试单元测试",
"type": "debugpy",
"request": "launch",
"program": "-m",
"args": [
"unittest",
"${file}"
],
"console": "integratedTerminal"
}
]
}
修改前后配置对比详解:
修改前配置解析:
{
"version": "0.2.0", // 配置文件的版本号,固定为0.2.0,这是VSCode调试配置的标准版本
"configurations": [ // 配置数组,可以包含多个调试配置
{
"name": "Python 调试程序: 当前文件", // 配置名称,显示在调试下拉列表中
"type": "debugpy", // 调试器类型,Python使用debugpy
"request": "launch", // 启动方式,launch表示启动新的调试会话
"program": "${file}", // 要调试的程序,${file}表示当前打开的文件
"console": "integratedTerminal" // 输出控制台类型,integratedTerminal表示使用VSCode内置终端
}
]
}
修改后配置解析:
{
"version": "0.2.0", // 版本号不变
"configurations": [
{
"name": "Python: 调试单元测试", // 修改了配置名称,更明确
"type": "debugpy", // 调试器类型不变
"request": "launch", // 启动方式不变
"program": "-m", // 重要改变:-m表示以模块方式运行Python代码
"args": [ // 新增:参数数组,传递给Python程序
"unittest", // 第1个参数:表示要运行unittest模块
"${file}" // 第2个参数:当前文件名
],
"console": "integratedTerminal" // 输出控制台不变
}
]
}
关键变化解释:
- 1.
program参数:从"${file}"改为"-m" - •
"${file}":直接运行当前Python文件 - •
"-m":以模块方式运行,配合args参数使用
- 2. 新增
args参数:["unittest", "${file}"] - • 这相当于在命令行执行:
python -m unittest test_debug.py - •
-m unittest:告诉Python以模块方式运行unittest
- • 修改前:直接运行Python脚本,不识别测试用例
- • 修改后:使用unittest框架运行,能自动发现并运行所有test_开头的测试方法
调试时的观察重点
1. 观察 test_addition:
- • 在
result = 1 + 2 处暂停后,按 F10 单步执行 - • 在
self.assertEqual(result, 3) 处,验证断言条件
2. 观察 test_division:
- • 观察异常如何被
assertRaises 上下文管理器捕获
3. 观察 test_list_operations:
- • 在
squared = [x**2 for x in items] 处设置断点
十、实用调试技巧
技巧1:最小化复现
当遇到bug时,不要试图调试整个大程序。先创建一个最小的测试用例:
# 原程序可能很复杂,但bug可能很简单
# 比如发现成绩计算不对,可以创建最小测试:
def test_calculation():
# 测试用例1:正常情况
scores = [80, 90, 100]
avg = sum(scores) / len(scores)
print(f"测试1 - 成绩: {scores}, 预期平均分: 90.0, 实际: {avg}")
# 测试用例2:边界情况
scores = []
try:
avg = sum(scores) / len(scores)
except ZeroDivisionError:
print("测试2 - 空列表会报错,需要处理!")
return "测试完成"
技巧2:二分法查找bug
如果不知道bug在哪,可以用二分法:
十一、常见问题与解决方案
问题1:调试器无法启动
可能原因:没有选择正确的Python解释器
解决方法:
- 2. 输入"Python: Select Interpreter"
问题2:断点不生效
可能原因:代码有语法错误
解决方法:
问题3:变量值显示为"undefined"
可能原因:该变量在当前作用域未定义
解决方法:
十二、调试工作流程总结
一个好的调试流程应该是:
- 2. 定位范围:猜测bug可能出现在哪个函数或代码块
- 4. 单步执行:使用F10/F11逐步执行,观察变量变化
十三、动手练习
现在轮到你了!尝试调试下面的代码:
def find_max_number(numbers):
# 这个函数应该返回列表中的最大数,但有bug
max_num = numbers[0]
for i in range(1, len(numbers)):
if numbers[i] > max_num:
numbers[i] = max_num # 这一行有问题!
return max_num
# 测试用例
test_data = [3, 7, 2, 9, 5]
result = find_max_number(test_data)
print(f"列表 {test_data} 中的最大值是: {result}")
print(f"正确结果应该是: {max(test_data)}")
执行结果:
列表 [3, 7, 2, 9, 5] 中的最大值是: 3
正确结果应该是: 9
🔍 挑战:使用今天学到的调试技巧,找出bug在哪里并修复它!
📌 小贴士汇总
Tip 1: 调试前先确保代码没有语法错误
Tip 2: 善用"单步跳过"(F10)提高调试效率
Tip 3: 在Watch窗口添加关键表达式,实时监控
Tip 4: 调试完成后记得清除不必要的断点
Tip 5: 复杂的bug可以先用print语句定位大致范围,再用调试器详细查看
十四、总结
今天我们一起学习了:
- 1. VSCode调试器的基本使用:如何启动调试、设置断点
- 2. 调试控制按钮的功能:继续、单步执行、单步跳过等
- 5. 单元测试的调试方法:如何配置和调试unittest测试用例
记住:调试不是猜谜游戏,而是一个科学的查找过程。通过今天学习的方法,你可以像侦探一样,一步步追踪bug的踪迹。
调试技能需要练习,建议你:
📦 资源获取提示
关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码,一起动手掌握高效编程的核心技巧!
❤️ 支持我们
如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!
📚 学习资源说明
本文内容是基于《Python Basics: A Practical Introduction to Python 3》(Real Python)一书的学习笔记整理。
这本书是一本非常优秀的Python入门教材,推荐给所有想要系统学习Python的朋友们。
这本书的特点:
跟着这本书学习,配合我的笔记整理,相信你能更快掌握Python编程!
让我们一起坚持学习,每天进步一点点!💪