03 - 调试与问题解决能力¶
目标:能独立定位和修复bug,不依赖AI
时间:1周
核心原则:调试是编程的核心技能,必须掌握
🐛 为什么调试能力重要?¶
现实场景¶
Text Only
场景1:代码报错
新手:"这段代码报错了,帮我修一下"(直接问AI)
老手:"我看看错误信息... 哦,是索引越界了"
场景2:程序结果不对
新手:"结果不对,哪里错了?"(直接问AI)
老手:"我加几个print看看中间值... 找到问题了"
场景3:程序卡住
新手:"程序没反应,怎么办?"(直接问AI)
老手:"我检查一下是不是死循环..."
调试能力 = 独立解决问题能力¶
- ✅ 不依赖AI,自己定位问题
- ✅ 快速修复bug
- ✅ 预防类似问题
- ✅ 深入理解代码逻辑
🎯 调试方法论¶
方法1:科学调试法¶
方法2:分而治之¶
Python
# 问题代码
def complex_function(data):
result = step1(data) # 可能出错
result = step2(result) # 可能出错
result = step3(result) # 可能出错
return result
# 调试方法:分段测试
print("After step1:", step1(data))
print("After step2:", step2(step1(data)))
print("After step3:", step3(step2(step1(data))))
方法3:最小复现¶
🛠️ 调试工具与技术¶
技术1:print调试法¶
最简单但最有效的调试方法:
Python
def calculate_average(numbers):
print(f"Input: {numbers}") # 查看输入
if not numbers:
print("Empty list!") # 检查边界
return 0
total = sum(numbers)
print(f"Sum: {total}") # 查看中间值
count = len(numbers)
print(f"Count: {count}") # 查看中间值
average = total / count
print(f"Average: {average}") # 查看结果
return average
技术2:断言(assert)¶
在关键点检查假设:
Python
def divide(a, b):
assert b != 0, "除数不能为0"
assert isinstance(a, (int, float)), "a必须是数字"
assert isinstance(b, (int, float)), "b必须是数字"
return a / b
# 使用
divide(10, 0) # AssertionError: 除数不能为0
技术3:日志记录¶
比print更专业的调试方法:
Python
import logging
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def process_data(data):
logging.debug(f"开始处理数据: {data}")
try: # try/except捕获异常
result = step1(data)
logging.info(f"步骤1完成: {result}")
result = step2(result)
logging.info(f"步骤2完成: {result}")
except Exception as e:
logging.error(f"处理失败: {e}")
raise
logging.debug(f"处理完成: {result}")
return result
技术4:使用IDE调试器¶
VS Code调试基础:
Text Only
1. 设置断点:点击行号左侧
2. 启动调试:F5
3. 单步执行:
- F10:单步跳过(不进入函数)
- F11:单步进入(进入函数)
- Shift+F11:单步跳出
4. 查看变量:左侧变量面板
5. 条件断点:右键断点设置条件
📝 常见错误与调试¶
错误类型1:语法错误¶
Python
# 错误代码
if x > 5
print("x is big")
# 错误信息
# SyntaxError: expected ':'
# 调试方法
# 1. 看错误提示的行号
# 2. 检查语法(漏了冒号、括号不匹配等)
错误类型2:运行时错误¶
Python
# 错误代码
def get_element(arr, index):
return arr[index]
get_element([1, 2, 3], 5)
# 错误信息
# IndexError: list index out of range
# 调试方法
# 1. 打印arr和index的值
# 2. 检查index是否在有效范围
# 3. 添加边界检查
修复:
Python
def get_element(arr, index):
print(f"Array: {arr}, Index: {index}, Length: {len(arr)}")
if index < 0 or index >= len(arr):
print(f"Error: Index {index} out of range [0, {len(arr)-1}]")
return None
return arr[index]
错误类型3:逻辑错误¶
Python
# 错误代码:计算1到n的和
def sum_n(n):
total = 0
for i in range(n): # 错误:应该是range(1, n+1)
total += i
return total
print(sum_n(5)) # 期望15,实际10
# 调试方法
# 1. 手动计算期望结果
# 2. 打印每次循环的值
# 3. 对比找出差异
调试:
Python
def sum_n(n):
total = 0
for i in range(n):
print(f"i={i}, total={total}") # 查看循环过程
total += i
print(f"Final total: {total}")
return total
sum_n(5)
# 输出:
# i=0, total=0
# i=1, total=0
# i=2, total=1
# i=3, total=3
# i=4, total=6
# Final total: 10
# 发现:i从0开始,不是从1开始
错误类型4:类型错误¶
Python
# 错误代码
def add(a, b):
return a + b
add("5", 3) # TypeError: can only concatenate str to str
# 调试方法
# 1. 打印参数类型
# 2. 添加类型检查
修复:
Python
def add(a, b):
print(f"a={a}, type={type(a)}")
print(f"b={b}, type={type(b)}")
if not isinstance(a, (int, float)):
a = float(a)
if not isinstance(b, (int, float)):
b = float(b)
return a + b
🎓 调试实战练习¶
练习1:找出并修复bug¶
Python
def find_max_index(arr):
"""找出数组中最大值的索引"""
max_index = 0
for i in range(len(arr)):
if arr[i] > arr[max_index]:
max_index = i
return max_index
# 测试
print(find_max_index([3, 1, 4, 1, 5, 9, 2, 6])) # 期望5,实际?
你的任务: 1. 先不看答案,自己找出bug 2. 用print调试 3. 修复bug
练习2:复杂bug调试¶
Python
def flatten(nested_list):
"""将嵌套列表展平"""
result = []
for item in nested_list:
if isinstance(item, list): # isinstance检查对象类型
result.extend(flatten(item))
else:
result.append(item)
return result
# 测试
test = [1, [2, 3], [4, [5, 6]], 7]
print(flatten(test)) # 期望[1, 2, 3, 4, 5, 6, 7]
问题:如果输入包含非列表可迭代对象(如字符串),会怎样?
练习3:性能问题调试¶
Python
def fibonacci(n):
"""计算斐波那契数列第n项"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print(fibonacci(35)) # 很慢!
问题:为什么慢?如何优化?
调试:
Python
call_count = 0
def fibonacci(n):
global call_count
call_count += 1
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(10)
print(f"Call count: {call_count}") # 看看调用了多少次
🎯 问题解决框架¶
框架:5W1H¶
遇到问题时,问自己:
| 问题 | 说明 |
|---|---|
| What | 什么现象?报错信息是什么? |
| When | 什么时候发生的? |
| Where | 在哪行代码?哪个函数? |
| Why | 为什么会这样? |
| Who | 哪个数据/变量导致的? |
| How | 如何修复?如何预防? |
示例应用¶
Text Only
问题:程序崩溃
What: IndexError: list index out of range
When: 运行到第15行时
Where: get_user函数
Why: 用户列表为空,但代码假设至少有一个用户
Who: users变量是空列表
How: 添加空列表检查
✅ 阶段检查¶
完成以下检查,确认你掌握了调试能力:
- 能读懂Python错误信息
- 能用print定位问题
- 能用断点调试
- 能写出最小复现代码
- 能独立修复常见bug
- 能预防常见错误
📚 下一步¶
完成调试能力训练后,进入 04-阅读源码技巧.md
记住:调试能力决定了一个程序员的上限。掌握调试,你就掌握了编程! 🔧