Module 8: 错误处理与调试
让代码更健壮 —— 优雅地处理异常
本章概览
编程时错误不可避免。文件不存在、数据格式错误、网络请求失败...如何让程序在出错时不崩溃,而是优雅地处理?本章将教你 Python 的异常处理机制,以及如何调试代码、找到并修复 bug。
重要提示:初学者常常害怕错误,但错误信息是你最好的老师!学会阅读错误信息,你就掌握了快速调试的关键。
学习目标
学完本章后,你将能够:
- 理解 Python 的常见错误类型
- 使用
try-except捕获和处理异常 - 编写健壮的文件读取和数据处理代码
- 使用
finally和else子句 - 自定义异常类
- 掌握调试技巧(print、断点、pdb)
- 阅读和理解错误追踪信息
章节内容
01 - 错误类型
核心问题: Python 有哪些常见错误?
核心内容:
- 语法错误 vs 运行时异常:
- 语法错误:代码写错,无法运行(缩进错误、冒号缺失)
- 运行时异常:代码可以运行,但执行时出错
- 常见运行时异常:
NameError:变量未定义pythonprint(age) # NameError: name 'age' is not definedTypeError:类型错误pythonresult = "25" + 5 # TypeError: can only concatenate strValueError:值错误pythonint("abc") # ValueError: invalid literal for int()KeyError:字典键不存在pythondata = {'age': 25} print(data['income']) # KeyError: 'income'IndexError:索引超出范围pythonlist = [1, 2, 3] print(list[10]) # IndexError: list index out of rangeFileNotFoundError:文件不存在pythonwith open('missing.txt', 'r') as f: # FileNotFoundError content = f.read()ZeroDivisionError:除零错误pythonresult = 10 / 0 # ZeroDivisionError: division by zero
实际应用:
python
# 数据分析常见错误
import pandas as pd
# 1. 文件不存在
df = pd.read_csv('data.csv') # FileNotFoundError
# 2. 列名错误
mean_income = df['income'].mean() # KeyError: 'income'
# 3. 类型转换错误
age = int(df['age'][0]) # ValueError: invalid literal02 - Try-Except 异常处理
核心问题: 如何捕获和处理错误?
核心内容:
- 基本语法:python
try: # 可能出错的代码 result = 10 / 0 except ZeroDivisionError: # 错误处理 print("不能除以零!") - 捕获多种异常:python
try: value = int(input("输入数字: ")) result = 10 / value except ValueError: print("输入的不是数字") except ZeroDivisionError: print("不能除以零") except Exception as e: print(f"其他错误: {e}") - else 和 finally 子句:python
try: file = open('data.txt', 'r') content = file.read() except FileNotFoundError: print("文件不存在") else: print("文件读取成功") # 无错误时执行 finally: file.close() # 总是执行 - 主动抛出异常:python
def calculate_income(hours, rate): if hours < 0: raise ValueError("工作时间不能为负数") return hours * rate
实际应用:
python
# 健壮的数据读取
import pandas as pd
def safe_read_csv(file_path, default_value=None):
"""安全地读取 CSV 文件"""
try:
df = pd.read_csv(file_path)
print(f"成功读取 {len(df)} 行数据")
return df
except FileNotFoundError:
print(f"错误: 文件 {file_path} 不存在")
return default_value
except pd.errors.EmptyDataError:
print(f"错误: 文件 {file_path} 是空的")
return default_value
except Exception as e:
print(f"未知错误: {e}")
return default_value
# 使用
df = safe_read_csv('survey.csv')
if df is not None:
# 继续分析...
pass03 - 调试技巧
核心问题: 如何找到和修复 bug?
核心内容:
- 阅读错误追踪信息(Traceback):python
Traceback (most recent call last): File "script.py", line 15, in <module> result = calculate_mean(data) File "script.py", line 8, in calculate_mean return sum(data) / len(data) ZeroDivisionError: division by zero- 从下往上读:最后一行是错误类型
- 中间是调用栈:哪个文件、哪一行、哪个函数
- Print 调试:python
def process_data(data): print(f"[DEBUG] 输入数据: {data}") # 检查输入 cleaned = [x for x in data if x > 0] print(f"[DEBUG] 清洗后: {cleaned}") # 检查中间结果 return sum(cleaned) / len(cleaned) - 使用 pdb 调试器:python
import pdb def calculate_mean(data): pdb.set_trace() # 断点 return sum(data) / len(data) - Jupyter 魔法命令:python
%debug # 进入最近错误的调试模式 %%time # 测量代码执行时间 %pdb on # 自动进入调试模式 - 常见调试策略:
- 二分法:注释掉一半代码,找到出错部分
- 简化输入:用最简单的数据测试
- 单元测试:编写小的测试用例
实际应用:
python
# 案例:调试数据清洗函数
import pandas as pd
def clean_survey_data(df):
"""清洗问卷数据"""
print(f"[DEBUG] 原始数据: {len(df)} 行, {len(df.columns)} 列")
# 删除缺失值
df_clean = df.dropna(subset=['age', 'income'])
print(f"[DEBUG] 删除缺失值后: {len(df_clean)} 行")
# 筛选年龄
df_clean = df_clean[(df_clean['age'] >= 18) & (df_clean['age'] <= 100)]
print(f"[DEBUG] 筛选年龄后: {len(df_clean)} 行")
# 删除异常收入
df_clean = df_clean[df_clean['income'] > 0]
print(f"[DEBUG] 删除异常收入后: {len(df_clean)} 行")
return df_clean错误处理策略对比
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 不处理 | 简单 | 程序崩溃 | 探索性分析、Jupyter 中 |
| try-except | 健壮 | 代码冗长 | 生产代码、批量处理 |
| 默认值 | 优雅 | 可能隐藏问题 | 可选参数、配置读取 |
| 记录日志 | 可追踪 | 需要额外代码 | 生产环境、长期项目 |
如何学习本章?
学习路线
第 1 天(2小时): 错误类型
- 阅读 01 - 错误类型
- 认识常见错误
- 学会阅读错误信息
第 2 天(3小时): Try-Except
- 阅读 02 - Try-Except
- 练习捕获异常
- 编写健壮的文件读取代码
第 3 天(2小时): 调试技巧
- 阅读 03 - 调试技巧
- 使用 print 调试
- 尝试 pdb 调试器
总时间: 7 小时(1 周)
最小化学习路径
对于初学者,优先级如下:
必学(基础技能,4小时):
- 01 - 错误类型(认识常见错误)
- 02 - Try-Except(基本语法)
- 阅读错误信息的技巧
重要(进阶技能,3小时):
- else 和 finally 子句
- Print 调试
- Jupyter 调试技巧
可选(高级主题):
- pdb 调试器
- 自定义异常类
- 日志记录(logging 模块)
学习建议
不要害怕错误
- 错误是学习的机会
- 每个错误都教你一个新知识点
- 专业程序员也会遇到大量错误
学会阅读错误信息
python# 错误信息示例 Traceback (most recent call last): File "script.py", line 15, in <module> # ← 从这里开始看 result = calculate_mean(data) File "script.py", line 8, in calculate_mean return sum(data) / len(data) # ← 这一行出错 ZeroDivisionError: division by zero # ← 这是错误类型 # 解读: # 1. 错误类型: ZeroDivisionError # 2. 出错位置: script.py 第 8 行 # 3. 原因: 除以零(len(data) = 0)渐进式错误处理
python# 阶段 1:不处理(探索性分析) df = pd.read_csv('data.csv') # 阶段 2:简单处理(脚本化) try: df = pd.read_csv('data.csv') except FileNotFoundError: print("文件不存在") # 阶段 3:完整处理(生产代码) def safe_read_csv(file_path): try: df = pd.read_csv(file_path) logger.info(f"成功读取 {len(df)} 行") return df except FileNotFoundError: logger.error(f"文件不存在: {file_path}") return None except Exception as e: logger.error(f"未知错误: {e}") raise常见场景的错误处理模式
python# 模式 1:文件读取 try: df = pd.read_csv('data.csv') except FileNotFoundError: df = pd.DataFrame() # 返回空 DataFrame # 模式 2:字典访问 value = data.get('key', default_value) # 更优雅 # 而不是 try: value = data['key'] except KeyError: value = default_value # 模式 3:类型转换 try: age = int(user_input) except ValueError: age = None # 或重新提示用户
常见问题
Q: 什么时候应该使用 try-except? A:
- 必须用:文件操作、网络请求、用户输入、类型转换
- 可以用:数据验证、可选功能
- 不推荐用:逻辑错误、算法bug(应该修复,不是隐藏)
Q: 为什么不推荐用 except Exception? A: 太宽泛,会捕获所有异常(包括你不想捕获的)。应该具体指定异常类型。
python
# 不推荐
try:
result = 10 / x
except Exception: # 太宽泛
pass
# 推荐
try:
result = 10 / x
except ZeroDivisionError: # 具体指定
result = 0Q: 如何在 Jupyter 中调试? A:
python
# 方法 1:Cell 抛出错误后
%debug # 进入调试模式
# 方法 2:自动调试
%pdb on # 之后所有错误都自动进入调试
# 方法 3:在代码中设置断点
import pdb; pdb.set_trace()Q: Print 调试 vs pdb 调试? A:
- Print:简单、快速、适合初学者
- pdb:强大、可交互、适合复杂 bug
Q: 错误处理会让代码变慢吗? A:
try块几乎没有性能开销- 只有在抛出异常时才会变慢
- 不要用异常控制正常流程
下一步
完成本章后,你将掌握:
- 认识和处理 Python 常见错误
- 使用 try-except 编写健壮代码
- 掌握调试技巧,快速定位 bug
- 阅读错误追踪信息
在 Module 9 中,我们将深入学习数据科学核心库(NumPy、Pandas、Matplotlib),这是数据分析的核心技能!
在 Module 10 中,我们将学习机器学习和 LLM API,探索 Python 的前沿应用。
错误不可怕,学会处理和调试才是关键!继续加油!