try/except,给程序装安全网
程序为什么”一碰就碎”?
上一篇我们给通讯录加上了文件保存功能,程序已经很好用了。但你有没有遇到过这种情况:
请输入选择:5
请输入年龄:abc
Traceback (most recent call last):
File "通讯录.py", line 15, in
年龄 = int("abc")
ValueError: invalid literal for int() with base 10: 'abc'
程序直接崩溃了。红色的报错信息看得人一头雾水。
这就像一个不会游泳的人跳进了深水区——没有救生圈,直接沉底。
我们需要一个”安全网”: 即使出了意外,程序也不会崩溃,而是优雅地告诉你:”出了一点小问题,但我会继续运转。”
这就是”错误处理”——Python的 try/except 机制。
用”安全网”来理解 try/except
想象你是一个走钢丝的杂技演员。没有安全网的时候,摔一跤就完了。有了安全网,即使失足,也能落到网上,爬起来继续走。
| 钢丝表演 | Python代码 | 含义 |
|---|
|—|—|—|
| 开始走钢丝 | try: |
把可能出错的代码放进来 |
|---|---|---|
| 失足摔下去 | 出错了 | 某行代码报错了 |
| 落到安全网上 | except: |
捕获错误,执行补救方案 |
| 继续表演 | 后面的代码 | 程序正常往下跑 |
最简单的写法:
try:
有可能出错的代码
except:
出错了怎么办
第一个 try/except
来看一个最简单的例子——用户输入数字:
try:
年龄 = int(input("请输入你的年龄:"))
print(f"你明年 {年龄 + 1} 岁")
except:
print("⚠️ 你输入的不是数字哦!")
运行效果:
请输入你的年龄:abc
⚠️ 你输入的不是数字哦!
注意:没有红色报错,没有崩溃! 程序友好地告诉你出了问题,然后继续运行。
常见的错误类型
程序出错有各种各样的”姿势”。Python给每种错误都起了名字:
ValueError —— 值不对
# 把不是数字的字符串转成数字
年龄 = int("abc") # ValueError: 不是合法的数字
TypeError —— 类型不对
# 字符串和数字不能直接相加
结果 = "年龄是" + 25 # TypeError: 字符串不能和数字相加
FileNotFoundError —— 文件不存在
# 打开一个不存在的文件
f = open("没有这个文件.txt") # FileNotFoundError
ZeroDivisionError —— 除以零
# 数学上不能除以零
结果 = 100 / 0 # ZeroDivisionError
KeyError —— 字典里没有这个键
通讯录 = {"张三": "13800001111"}
电话 = 通讯录["李四"] # KeyError: '李四'
IndexError —— 列表索引越界
成绩 = [90, 85, 78]
print(成绩[5]) # IndexError: 列表只有3个元素
分门别类捕获——多个 except
有时候 try 里面可能出不同的错,我们需要分别处理:
try:
年龄 = int(input("请输入年龄:"))
结果 = 100 / 年龄
print(f"结果是 {结果}")
except ValueError:
print("⚠️ 请输入数字,不是文字!")
except ZeroDivisionError:
print("⚠️ 年龄不能是0!")
except:
print("⚠️ 出了未知错误")
顺序很重要: 先写具体的错误类型,最后写 except:(兜底)。就像医院分诊——先去对应科室,实在不行才去急诊。
else 和 finally
try/except 还有两个小伙伴:
try:
年龄 = int(input("请输入年龄:"))
except ValueError:
print("输入有误!")
else:
print(f"输入成功,你 {年龄} 岁")
finally:
print("不管成功失败,我都会执行")
| 关键字 | 什么时候执行 | 类比 |
|---|
|—|—|—|
try |
尝试执行 | 走钢丝 |
|---|---|---|
except |
出错时执行 | 落到安全网上 |
else |
没出错时执行 | 成功走完,观众鼓掌 |
finally |
无论如何都执行 | 表演结束,收拾道具 |
finally 最常见的用途是:确保资源被释放。比如打开了文件,不管有没有出错,都要关掉它。
主动抛出错误——raise
有时候你需要”故意”引发一个错误,来提醒使用者:
def 设置年龄(年龄):
if 年龄 150:
raise ValueError("年龄不能超过150岁!")
print(f"年龄设置为 {年龄}")
try:
设置年龄(-5)
except ValueError as 错误:
print(f"❌ {错误}")
# 输出:❌ 年龄不能是负数!
raise 就是你主动说:”这不对!”——然后交给 except 去处理。
实战:让通讯录程序”百毒不侵”
现在把学到的错误处理用到通讯录上。这个版本的通讯录,不管用户怎么乱输入,都不会崩溃:
import json
文件名 = "通讯录.json"
def 读取通讯录():
try:
with open(文件名, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
return {} # 文件不存在,返回空字典
except json.JSONDecodeError:
return {} # 文件内容坏了,也返回空字典
def 保存通讯录(通讯录):
with open(文件名, "w", encoding="utf-8") as f:
json.dump(通讯录, f, ensure_ascii=False, indent=2)
通讯录 = 读取通讯录()
while True:
print("n=== 通讯录 ===")
print("1. 查看所有联系人")
print("2. 添加联系人")
print("3. 搜索联系人")
print("4. 删除联系人")
print("5. 退出")
选择 = input("请输入选择(1-5):")
if 选择 == "1":
if not 通讯录:
print("📭 通讯录为空")
else:
for 名字, 信息 in 通讯录.items():
print(f" {名字}:{信息['电话']}")
elif 选择 == "2":
名字 = input("姓名:").strip()
if not 名字:
print("⚠️ 姓名不能为空")
continue
电话 = input("电话:").strip()
if not 电话:
print("⚠️ 电话不能为空")
continue
通讯录[名字] = {"电话": 电话}
保存通讯录(通讯录)
print(f"✅ 已添加 {名字}")
elif 选择 == "3":
关键字 = input("输入要搜索的姓名:").strip()
try:
信息 = 通讯录[关键字]
print(f" {关键字}:{信息['电话']}")
except KeyError:
print(f" ❌ 找不到「{关键字}」")
elif 选择 == "4":
名字 = input("要删除谁:").strip()
try:
del 通讯录[名字]
保存通讯录(通讯录)
print(f"✅ 已删除 {名字}")
except KeyError:
print(f" ❌ 找不到「{名字}」")
elif 选择 == "5":
print("👋 再见!")
break
else:
print("⚠️ 请输入 1-5 的数字")
这个版本做了哪些”防崩溃”处理?
| 用户可能的错误 | 处理方式 |
|---|
|—|—|
| 文件不存在 | FileNotFoundError → 返回空字典 |
|---|---|
| 输入了 6、7、8 这种无效选项 | else 提示”请输入1-5″ |
| 姓名/电话留空 | strip() + if not 检查 |
| 搜索不存在的人 | KeyError → 提示”找不到” |
| 删除不存在的人 | KeyError → 提示”找不到” |
不管用户怎么乱来,程序都不会崩溃。 这就是一个”百毒不侵”的程序。
小结
| 概念 | 作用 | 安全网类比 |
|---|
|—|—|—|
try/except |
捕获错误,防止崩溃 | 安全网接住掉落的人 |
|---|---|---|
多个 except |
分别处理不同错误 | 不同科室看不同病 |
else |
没出错时执行 | 成功表演后观众鼓掌 |
finally |
无论如何都执行 | 散场后收拾舞台 |
raise |
主动引发错误 | 发现问题主动叫停 |
| 常见错误类型 | ValueError、TypeError、KeyError、FileNotFoundError 等 |
各种”摔倒姿势” |
核心原则: 对用户的输入永远不信任,对文件和外部资源永远做好”不存在”的准备。加几行 try/except,程序就从”玻璃心”变成”钢铁侠”。
下篇预告
《AI编程(十一):AI帮你选技术栈》
写了这么多程序,你有没有想过:Python能做网站吗?能做游戏吗?能做手机App吗?答案是——能,但每种需求都有最合适的技术栈。下一篇我们让AI帮你分析需求、推荐工具,从”会写代码”升级到”知道用什么写代码”。
发表回复