Files

342 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 2-3 课:异常处理
## 一、本课目标
完成本课后,你将能够:
1. 解释异常是什么,以及为什么需要处理异常;
2. 阅读 Python 异常信息中的异常类型;
3. 使用 `try...except` 处理预期内的错误;
4. 分别处理 `ValueError``ZeroDivisionError``FileNotFoundError`
5. 理解 `else``finally` 的执行时机;
6. 避免使用过大的 `try` 范围或空白 `except`
7. 让文件读取和数据转换失败时给出清楚的中文提示。
## 二、前置知识
学习本课前,需要掌握:
- 条件判断、函数和返回值;
- `int()` 类型转换;
- 模块与程序入口判断;
- 使用 `Path` 读取和写入文本文件。
## 三、异常是什么
异常Exception是程序运行过程中发生的非正常情况。
例如:
```python
number = int("")
```
运行后会看到类似信息:
```text
ValueError: invalid literal for int() with base 10: '五'
```
中文解释:`int()` 需要可以转换为整数的内容,但字符串 `"五"` 不符合要求。
如果不处理异常,程序会在出错位置停止。异常处理可以让程序识别预期内的问题,给用户明确提示,并决定是否继续运行。
异常处理不是“隐藏所有错误”。我们只处理已经理解并且知道如何应对的异常。
## 四、使用 `try...except`
基本结构如下:
```python
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 Error`ValueError`)表示值的类型可能可以使用,但具体内容不符合要求:
```python
int("优秀")
```
### 5.2 `ZeroDivisionError`
除零错误Zero Division Error`ZeroDivisionError`)表示程序尝试用数字除以零:
```python
average = 180 / 0
```
### 5.3 `FileNotFoundError`
文件未找到错误File Not Found Error`FileNotFoundError`)表示程序读取的路径不存在:
```python
content = file_path.read_text(encoding="utf-8")
```
不推荐写空白 `except`
```python
try:
number = int(number_text)
except:
print("出错了。")
```
这种写法会把多种不同问题混在一起,用户不知道发生了什么,开发者也更难找到真正原因。
## 六、一个 `try` 对应多个 `except`
一段代码可能出现不同异常,可以分别处理:
```python
try:
content = file_path.read_text(encoding="utf-8")
number = int(content.strip())
except FileNotFoundError:
print("没有找到文件。")
except ValueError:
print("文件内容不是有效整数。")
```
Python 会根据实际异常类型进入匹配的分支。清楚区分异常类型,才能给出准确提示。
## 七、`else` 的作用
`else` 只在 `try` 没有发生异常时执行:
```python
try:
average = total_score / agent_count
except ZeroDivisionError:
print("Agent 数量不能为 0。")
else:
return average
```
这样可以让 `try` 只包含可能发生异常的操作,成功后的普通逻辑放到 `else`
## 八、`finally` 的作用
`finally` 无论有没有发生异常都会执行:
```python
try:
content = file_path.read_text(encoding="utf-8")
except FileNotFoundError:
print("没有找到文件。")
finally:
print("文件读取结束。")
```
它常用于必须执行的收尾工作。本课用输出语句观察执行时机。上一课使用的 `with` 已经能帮助关闭文件,因此不要为了使用 `finally` 而放弃更清晰的 `with``Path.read_text()`
## 九、异常与条件判断的区别
条件判断适合处理程序能够直接检查的业务规则:
```python
if score < 0 or score > 100:
print("评分必须在 0 到 100 之间。")
```
异常处理适合处理操作执行时可能失败的情况:
```python
try:
score = int(score_text)
except ValueError:
print("评分必须是整数。")
```
因此,评分转换失败使用 `except ValueError`,评分超出范围使用 `if`,两者各自负责不同问题。
## 十、缩小 `try` 的范围
推荐:
```python
try:
number = int(number_text)
except ValueError:
return None
print("转换完成。")
```
不要把大量无关代码全部放进 `try`。范围过大时,即使其他代码产生同类型异常,也可能被误认为是预期问题。
## 十一、完整示例
本课示例文件是:
```text
02_python进阶/2_3_异常处理/exception_example.py
```
示例依次演示:
1. 使用 `try...except` 处理整数转换失败;
2. 使用 `else` 返回成功的除法结果;
3. 分别处理正常文件和不存在的文件;
4. 使用 `finally` 输出每次读取结束提示。
示例只在本课 `example_data` 中创建文件,该目录已由 `.gitignore` 忽略。
## 十二、运行方法
在项目根目录运行:
```powershell
python .\02_python进阶\2_3_异常处理\exception_example.py
```
完成练习后运行:
```powershell
python .\02_python进阶\2_3_异常处理\practice.py
```
## 十三、示例运行结果
```text
一、处理整数转换异常
“五”不是有效整数。
有效结果5
无效结果None
==============================
二、处理除以零异常
Agent 数量不能为 0。
正常结果2.0
异常结果None
==============================
三、处理文件读取异常
已完成对 agent_count.txt 的读取尝试。
没有找到文件missing.txt
已完成对 missing.txt 的读取尝试。
文件中的 Agent 数量3
不存在文件的读取结果None
```
## 十四、关键代码解析
### 14.1 异常类型写在 `except` 后
```python
except ValueError:
```
它表示这里只处理 `ValueError`。如果发生其他没有处理的异常Python 仍会显示异常信息,帮助我们发现新的问题。
### 14.2 异常分支也要明确返回
如果函数成功时返回数字,失败时可以明确返回 `None`
```python
except ValueError:
print("请输入有效整数。")
return None
```
调用者随后可以使用:
```python
if result is not None:
print(result)
```
注意不能简单写 `if result:`,因为合法数字 `0` 也会被当作假值。
### 14.3 `return` 后仍会执行 `finally`
即使 `try``except` 中执行了 `return`,函数真正结束前仍会执行 `finally`。可以运行示例观察这一点。
## 十五、常见错误
### 15.1 使用空白 `except`
空白 `except` 会捕获范围过广,让真正的程序错误难以发现。请写出明确异常类型。
### 15.2 捕获异常后什么都不做
不推荐:
```python
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` 无论是否发生异常都会执行;
- `ValueError``ZeroDivisionError``FileNotFoundError` 对应不同问题;
- 异常处理用于应对失败,条件判断用于检查业务规则;
- 不要使用空白 `except` 隐藏问题。
## 十九、验收标准
- 能用自己的话解释异常处理的作用;
- 能看懂三个本课异常类型;
- 示例程序输出与讲义一致;
- 能针对不同错误使用明确的 `except`
- 能说明 `else``finally` 的执行时机;
- `parse_score("0")``parse_score("100")` 均能返回合法结果;
- 非数字和越界评分会给出不同中文提示;
- 除数为零时程序不会崩溃;
- 文件不存在时程序不会崩溃;
- 练习只在 `practice_data` 中写入数据;
- 没有使用空白 `except` 或用 `pass` 隐藏异常。