Skip to content

Module 8: 错误处理与调试

让代码更健壮 —— 优雅地处理异常


本章概览

编程时错误不可避免。文件不存在、数据格式错误、网络请求失败...如何让程序在出错时不崩溃,而是优雅地处理?本章将教你 Python 的异常处理机制,以及如何调试代码、找到并修复 bug。

重要提示:初学者常常害怕错误,但错误信息是你最好的老师!学会阅读错误信息,你就掌握了快速调试的关键。


学习目标

学完本章后,你将能够:

  • 理解 Python 的常见错误类型
  • 使用 try-except 捕获和处理异常
  • 编写健壮的文件读取和数据处理代码
  • 使用 finallyelse 子句
  • 自定义异常类
  • 掌握调试技巧(print、断点、pdb)
  • 阅读和理解错误追踪信息

章节内容

01 - 错误类型

核心问题: Python 有哪些常见错误?

核心内容:

  • 语法错误 vs 运行时异常:
    • 语法错误:代码写错,无法运行(缩进错误、冒号缺失)
    • 运行时异常:代码可以运行,但执行时出错
  • 常见运行时异常:
    • NameError:变量未定义
      python
      print(age)  # NameError: name 'age' is not defined
    • TypeError:类型错误
      python
      result = "25" + 5  # TypeError: can only concatenate str
    • ValueError:值错误
      python
      int("abc")  # ValueError: invalid literal for int()
    • KeyError:字典键不存在
      python
      data = {'age': 25}
      print(data['income'])  # KeyError: 'income'
    • IndexError:索引超出范围
      python
      list = [1, 2, 3]
      print(list[10])  # IndexError: list index out of range
    • FileNotFoundError:文件不存在
      python
      with open('missing.txt', 'r') as f:  # FileNotFoundError
          content = f.read()
    • ZeroDivisionError:除零错误
      python
      result = 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 literal

02 - 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:
    # 继续分析...
    pass

03 - 调试技巧

核心问题: 如何找到和修复 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 模块)

学习建议

  1. 不要害怕错误

    • 错误是学习的机会
    • 每个错误都教你一个新知识点
    • 专业程序员也会遇到大量错误
  2. 学会阅读错误信息

    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)
  3. 渐进式错误处理

    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
  4. 常见场景的错误处理模式

    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 = 0

Q: 如何在 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 的前沿应用。

错误不可怕,学会处理和调试才是关键!继续加油!


快速链接

基于 MIT 许可证发布。内容版权归作者所有。