Files

9.2 KiB
Raw Permalink Blame History

第 2-3 课:异常处理

一、本课目标

完成本课后,你将能够:

  1. 解释异常是什么,以及为什么需要处理异常;
  2. 阅读 Python 异常信息中的异常类型;
  3. 使用 try...except 处理预期内的错误;
  4. 分别处理 ValueErrorZeroDivisionErrorFileNotFoundError
  5. 理解 elsefinally 的执行时机;
  6. 避免使用过大的 try 范围或空白 except
  7. 让文件读取和数据转换失败时给出清楚的中文提示。

二、前置知识

学习本课前,需要掌握:

  • 条件判断、函数和返回值;
  • int() 类型转换;
  • 模块与程序入口判断;
  • 使用 Path 读取和写入文本文件。

三、异常是什么

异常Exception是程序运行过程中发生的非正常情况。

例如:

number = int("五")

运行后会看到类似信息:

ValueError: invalid literal for int() with base 10: '五'

中文解释:int() 需要可以转换为整数的内容,但字符串 "五" 不符合要求。

如果不处理异常,程序会在出错位置停止。异常处理可以让程序识别预期内的问题,给用户明确提示,并决定是否继续运行。

异常处理不是“隐藏所有错误”。我们只处理已经理解并且知道如何应对的异常。

四、使用 try...except

基本结构如下:

try:
    number = int(number_text)
except ValueError:
    print("请输入有效整数。")

执行顺序:

  1. Python 先执行 try 中的代码;
  2. 没有异常时,跳过 except
  3. 发生 ValueError 时,停止执行 try 中剩余代码;
  4. 转到对应的 except ValueError
  5. except 执行结束后,程序可以继续向下运行。

五、只捕获明确的异常

本课会遇到三种常见异常。

5.1 ValueError

值错误Value ErrorValueError)表示值的类型可能可以使用,但具体内容不符合要求:

int("优秀")

5.2 ZeroDivisionError

除零错误Zero Division ErrorZeroDivisionError)表示程序尝试用数字除以零:

average = 180 / 0

5.3 FileNotFoundError

文件未找到错误File Not Found ErrorFileNotFoundError)表示程序读取的路径不存在:

content = file_path.read_text(encoding="utf-8")

不推荐写空白 except

try:
    number = int(number_text)
except:
    print("出错了。")

这种写法会把多种不同问题混在一起,用户不知道发生了什么,开发者也更难找到真正原因。

六、一个 try 对应多个 except

一段代码可能出现不同异常,可以分别处理:

try:
    content = file_path.read_text(encoding="utf-8")
    number = int(content.strip())
except FileNotFoundError:
    print("没有找到文件。")
except ValueError:
    print("文件内容不是有效整数。")

Python 会根据实际异常类型进入匹配的分支。清楚区分异常类型,才能给出准确提示。

七、else 的作用

else 只在 try 没有发生异常时执行:

try:
    average = total_score / agent_count
except ZeroDivisionError:
    print("Agent 数量不能为 0。")
else:
    return average

这样可以让 try 只包含可能发生异常的操作,成功后的普通逻辑放到 else

八、finally 的作用

finally 无论有没有发生异常都会执行:

try:
    content = file_path.read_text(encoding="utf-8")
except FileNotFoundError:
    print("没有找到文件。")
finally:
    print("文件读取结束。")

它常用于必须执行的收尾工作。本课用输出语句观察执行时机。上一课使用的 with 已经能帮助关闭文件,因此不要为了使用 finally 而放弃更清晰的 withPath.read_text()

九、异常与条件判断的区别

条件判断适合处理程序能够直接检查的业务规则:

if score < 0 or score > 100:
    print("评分必须在 0 到 100 之间。")

异常处理适合处理操作执行时可能失败的情况:

try:
    score = int(score_text)
except ValueError:
    print("评分必须是整数。")

因此,评分转换失败使用 except ValueError,评分超出范围使用 if,两者各自负责不同问题。

十、缩小 try 的范围

推荐:

try:
    number = int(number_text)
except ValueError:
    return None

print("转换完成。")

不要把大量无关代码全部放进 try。范围过大时,即使其他代码产生同类型异常,也可能被误认为是预期问题。

十一、完整示例

本课示例文件是:

02_python进阶/2_3_异常处理/exception_example.py

示例依次演示:

  1. 使用 try...except 处理整数转换失败;
  2. 使用 else 返回成功的除法结果;
  3. 分别处理正常文件和不存在的文件;
  4. 使用 finally 输出每次读取结束提示。

示例只在本课 example_data 中创建文件,该目录已由 .gitignore 忽略。

十二、运行方法

在项目根目录运行:

python .\02_python进阶\2_3_异常处理\exception_example.py

完成练习后运行:

python .\02_python进阶\2_3_异常处理\practice.py

十三、示例运行结果

一、处理整数转换异常
“五”不是有效整数。
有效结果5
无效结果None
==============================
二、处理除以零异常
Agent 数量不能为 0。
正常结果2.0
异常结果None
==============================
三、处理文件读取异常
已完成对 agent_count.txt 的读取尝试。
没有找到文件missing.txt
已完成对 missing.txt 的读取尝试。
文件中的 Agent 数量3
不存在文件的读取结果None

十四、关键代码解析

14.1 异常类型写在 except

except ValueError:

它表示这里只处理 ValueError。如果发生其他没有处理的异常Python 仍会显示异常信息,帮助我们发现新的问题。

14.2 异常分支也要明确返回

如果函数成功时返回数字,失败时可以明确返回 None

except ValueError:
    print("请输入有效整数。")
    return None

调用者随后可以使用:

if result is not None:
    print(result)

注意不能简单写 if result:,因为合法数字 0 也会被当作假值。

14.3 return 后仍会执行 finally

即使 tryexcept 中执行了 return,函数真正结束前仍会执行 finally。可以运行示例观察这一点。

十五、常见错误

15.1 使用空白 except

空白 except 会捕获范围过广,让真正的程序错误难以发现。请写出明确异常类型。

15.2 捕获异常后什么都不做

不推荐:

except ValueError:
    pass

用户看不到提示,开发者也不知道数据为什么没有得到处理。

15.3 用异常代替全部条件判断

try...except 不能取代正常业务判断。数字是否在 0 到 100 之间仍应使用 if 检查。

15.4 把 try 包住整个程序

这会让异常来源变得模糊。应只包住可能发生且准备处理的操作。

15.5 把英文错误信息原样交给用户

学习时可以阅读英文异常类型,但面向用户的提示应说明他们可以如何修正输入。

十六、课堂练习

打开 practice.py,依次完成:

  1. parse_score(score_text):处理整数转换并检查评分范围;
  2. calculate_average(total_score, agent_count):处理除以零;
  3. read_score(file_path):处理不存在的评分文件;
  4. main():准备安全的练习数据并测试正常、异常分支。

练习题面已经明确给出了参数、返回值、输出文字、测试输入和预期结果。请先独立完成,不要直接复制示例函数。

十七、参考答案

参考答案暂不写入练习文件。你完成后,我会从以下三个方面检查:

  • 正确性:正常和异常分支是否符合预期;
  • 可读性:try 范围、命名和中文提示是否清楚;
  • 知识掌握:是否能正确选择异常类型,并区分异常处理与条件判断。

十八、本课小结

  • 异常是程序运行时出现的非正常情况;
  • try 放置可能发生异常的代码;
  • except 处理明确类型的异常;
  • else 只在没有异常时执行;
  • finally 无论是否发生异常都会执行;
  • ValueErrorZeroDivisionErrorFileNotFoundError 对应不同问题;
  • 异常处理用于应对失败,条件判断用于检查业务规则;
  • 不要使用空白 except 隐藏问题。

十九、验收标准

  • 能用自己的话解释异常处理的作用;
  • 能看懂三个本课异常类型;
  • 示例程序输出与讲义一致;
  • 能针对不同错误使用明确的 except
  • 能说明 elsefinally 的执行时机;
  • parse_score("0")parse_score("100") 均能返回合法结果;
  • 非数字和越界评分会给出不同中文提示;
  • 除数为零时程序不会崩溃;
  • 文件不存在时程序不会崩溃;
  • 练习只在 practice_data 中写入数据;
  • 没有使用空白 except 或用 pass 隐藏异常。