前面几篇文章里的程序,大多有一个共同问题。
数据只存在内存里。
程序一关,用户输入过什么,计算出什么结果,全都没了。
真实工具不可能这样。它需要读取文件、保存数据、处理错误,遇到问题时给出明确提示,而不是直接崩溃。
这篇文章讲三个很实用的能力:
- 文件读写。
- JSON 数据保存。
- 异常处理。
学完之后,你就能写一个能保存数据的小工具。
文件路径先讲清楚
文件读写最容易出问题的不是语法,而是路径。
看这行:
python
with open("note.txt", "r", encoding="utf-8") as file:
content = file.read()
note.txt 是相对路径。
它不是永远相对于代码文件所在目录,而是相对于程序运行时的当前工作目录。
这就是为什么有时候你明明把文件放在旁边,程序却说找不到。
更稳的方式是用 pathlib:
python
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
note_path = BASE_DIR / "note.txt"
with note_path.open("r", encoding="utf-8") as file:
content = file.read()
print(content)
Path(__file__).resolve().parent 表示当前 Python 文件所在目录。
这样不管你从哪里运行程序,读取的都是代码文件旁边的 note.txt。
文件路径关系可以画成这样:
#mermaid-svg-AcrDdo6lPxMr0gU6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AcrDdo6lPxMr0gU6 .error-icon{fill:#552222;}#mermaid-svg-AcrDdo6lPxMr0gU6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AcrDdo6lPxMr0gU6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .marker.cross{stroke:#333333;}#mermaid-svg-AcrDdo6lPxMr0gU6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AcrDdo6lPxMr0gU6 p{margin:0;}#mermaid-svg-AcrDdo6lPxMr0gU6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster-label text{fill:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster-label span{color:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster-label span p{background-color:transparent;}#mermaid-svg-AcrDdo6lPxMr0gU6 .label text,#mermaid-svg-AcrDdo6lPxMr0gU6 span{fill:#333;color:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .node rect,#mermaid-svg-AcrDdo6lPxMr0gU6 .node circle,#mermaid-svg-AcrDdo6lPxMr0gU6 .node ellipse,#mermaid-svg-AcrDdo6lPxMr0gU6 .node polygon,#mermaid-svg-AcrDdo6lPxMr0gU6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .rough-node .label text,#mermaid-svg-AcrDdo6lPxMr0gU6 .node .label text,#mermaid-svg-AcrDdo6lPxMr0gU6 .image-shape .label,#mermaid-svg-AcrDdo6lPxMr0gU6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-AcrDdo6lPxMr0gU6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .rough-node .label,#mermaid-svg-AcrDdo6lPxMr0gU6 .node .label,#mermaid-svg-AcrDdo6lPxMr0gU6 .image-shape .label,#mermaid-svg-AcrDdo6lPxMr0gU6 .icon-shape .label{text-align:center;}#mermaid-svg-AcrDdo6lPxMr0gU6 .node.clickable{cursor:pointer;}#mermaid-svg-AcrDdo6lPxMr0gU6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .arrowheadPath{fill:#333333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AcrDdo6lPxMr0gU6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AcrDdo6lPxMr0gU6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AcrDdo6lPxMr0gU6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster text{fill:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 .cluster span{color:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AcrDdo6lPxMr0gU6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AcrDdo6lPxMr0gU6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-AcrDdo6lPxMr0gU6 .icon-shape,#mermaid-svg-AcrDdo6lPxMr0gU6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AcrDdo6lPxMr0gU6 .icon-shape p,#mermaid-svg-AcrDdo6lPxMr0gU6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AcrDdo6lPxMr0gU6 .icon-shape .label rect,#mermaid-svg-AcrDdo6lPxMr0gU6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AcrDdo6lPxMr0gU6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AcrDdo6lPxMr0gU6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AcrDdo6lPxMr0gU6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 运行命令所在目录
相对路径查找位置
代码文件所在目录
Path(file).parent
可能和代码目录不同
更适合定位项目内文件
文本文件怎么写
写入文件:
python
from pathlib import Path
note_path = Path("note.txt")
with note_path.open("w", encoding="utf-8") as file:
file.write("今天学习 Python 文件读写")
w 表示写入。文件不存在会创建,文件存在会覆盖。
追加内容:
python
from pathlib import Path
note_path = Path("note.txt")
with note_path.open("a", encoding="utf-8") as file:
file.write("\n继续学习 JSON 和异常处理")
a 表示追加,不会覆盖原内容。
读取文件:
python
from pathlib import Path
note_path = Path("note.txt")
with note_path.open("r", encoding="utf-8") as file:
content = file.read()
print(content)
这里建议始终使用 with。
原因是 with 代码块结束后,文件会自动关闭。你不用手动写 close(),也不容易忘。
read、readline、readlines 怎么选
一次性读取全部内容:
python
with open("note.txt", "r", encoding="utf-8") as file:
content = file.read()
逐行读取:
python
with open("note.txt", "r", encoding="utf-8") as file:
for line in file:
print(line.strip())
读取成列表:
python
with open("note.txt", "r", encoding="utf-8") as file:
lines = file.readlines()
print(lines)
选择标准:
小文件可以 read()。
大文件更适合逐行读取,避免一次性占用太多内存。
如果后续确实要按行组成列表,再用 readlines()。
为什么总要写 encoding="utf-8"
如果文件里有中文,编码必须认真对待。
python
with open("note.txt", "w", encoding="utf-8") as file:
file.write("你好,Python")
不同系统默认编码可能不同。
你不写 encoding,在自己电脑上可能没事,换一台电脑就乱码。
所以中文文件统一写 encoding="utf-8",这是一个好习惯。
如果 CSV 要给 Excel 打开,有时可以用 utf-8-sig,但普通文本和 JSON 用 utf-8 就很好。
JSON 是什么
JSON 是一种保存结构化数据的文本格式。
它长得像 Python 字典,但不是 Python 字典。
JSON 文本:
json
{
"name": "小明",
"age": 18,
"skills": ["Python", "AI"]
}
Python 数据:
python
user = {
"name": "小明",
"age": 18,
"skills": ["Python", "AI"]
}
二者可以互相转换:
#mermaid-svg-Smim3JO97gXG1SPH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Smim3JO97gXG1SPH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Smim3JO97gXG1SPH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Smim3JO97gXG1SPH .error-icon{fill:#552222;}#mermaid-svg-Smim3JO97gXG1SPH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Smim3JO97gXG1SPH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Smim3JO97gXG1SPH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Smim3JO97gXG1SPH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Smim3JO97gXG1SPH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Smim3JO97gXG1SPH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Smim3JO97gXG1SPH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Smim3JO97gXG1SPH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Smim3JO97gXG1SPH .marker.cross{stroke:#333333;}#mermaid-svg-Smim3JO97gXG1SPH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Smim3JO97gXG1SPH p{margin:0;}#mermaid-svg-Smim3JO97gXG1SPH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Smim3JO97gXG1SPH .cluster-label text{fill:#333;}#mermaid-svg-Smim3JO97gXG1SPH .cluster-label span{color:#333;}#mermaid-svg-Smim3JO97gXG1SPH .cluster-label span p{background-color:transparent;}#mermaid-svg-Smim3JO97gXG1SPH .label text,#mermaid-svg-Smim3JO97gXG1SPH span{fill:#333;color:#333;}#mermaid-svg-Smim3JO97gXG1SPH .node rect,#mermaid-svg-Smim3JO97gXG1SPH .node circle,#mermaid-svg-Smim3JO97gXG1SPH .node ellipse,#mermaid-svg-Smim3JO97gXG1SPH .node polygon,#mermaid-svg-Smim3JO97gXG1SPH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Smim3JO97gXG1SPH .rough-node .label text,#mermaid-svg-Smim3JO97gXG1SPH .node .label text,#mermaid-svg-Smim3JO97gXG1SPH .image-shape .label,#mermaid-svg-Smim3JO97gXG1SPH .icon-shape .label{text-anchor:middle;}#mermaid-svg-Smim3JO97gXG1SPH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Smim3JO97gXG1SPH .rough-node .label,#mermaid-svg-Smim3JO97gXG1SPH .node .label,#mermaid-svg-Smim3JO97gXG1SPH .image-shape .label,#mermaid-svg-Smim3JO97gXG1SPH .icon-shape .label{text-align:center;}#mermaid-svg-Smim3JO97gXG1SPH .node.clickable{cursor:pointer;}#mermaid-svg-Smim3JO97gXG1SPH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Smim3JO97gXG1SPH .arrowheadPath{fill:#333333;}#mermaid-svg-Smim3JO97gXG1SPH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Smim3JO97gXG1SPH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Smim3JO97gXG1SPH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Smim3JO97gXG1SPH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Smim3JO97gXG1SPH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Smim3JO97gXG1SPH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Smim3JO97gXG1SPH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Smim3JO97gXG1SPH .cluster text{fill:#333;}#mermaid-svg-Smim3JO97gXG1SPH .cluster span{color:#333;}#mermaid-svg-Smim3JO97gXG1SPH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Smim3JO97gXG1SPH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Smim3JO97gXG1SPH rect.text{fill:none;stroke-width:0;}#mermaid-svg-Smim3JO97gXG1SPH .icon-shape,#mermaid-svg-Smim3JO97gXG1SPH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Smim3JO97gXG1SPH .icon-shape p,#mermaid-svg-Smim3JO97gXG1SPH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Smim3JO97gXG1SPH .icon-shape .label rect,#mermaid-svg-Smim3JO97gXG1SPH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Smim3JO97gXG1SPH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Smim3JO97gXG1SPH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Smim3JO97gXG1SPH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} json.dump / json.dumps
json.load / json.loads
Python dict / list
JSON 文本
dump、dumps、load、loads
这四个函数名字很像,但规则很简单。
带 s 的处理字符串。
不带 s 的处理文件。
写入 JSON 文件:
python
import json
user = {
"name": "小明",
"age": 18,
"skills": ["Python", "AI"]
}
with open("user.json", "w", encoding="utf-8") as file:
json.dump(user, file, ensure_ascii=False, indent=2)
ensure_ascii=False 可以让中文正常保存。
indent=2 让文件更容易阅读。
读取 JSON 文件:
python
import json
with open("user.json", "r", encoding="utf-8") as file:
user = json.load(file)
print(user["name"])
转成 JSON 字符串:
python
import json
user = {"name": "小明", "age": 18}
text = json.dumps(user, ensure_ascii=False)
print(text)
从 JSON 字符串解析:
python
import json
text = '{"name": "小明", "age": 18}'
user = json.loads(text)
print(user["age"])
JSON 类型映射
| Python | JSON |
|---|---|
dict |
object |
list / tuple |
array |
str |
string |
int / float |
number |
True |
true |
False |
false |
None |
null |
注意,JSON 里没有元组,Python 元组保存成 JSON 后会变成数组,读回来通常是列表。
异常是什么
异常就是程序运行过程中遇到的问题。
比如:
python
age = int(input("请输入年龄:"))
print(age + 1)
如果用户输入 十八,就会抛出 ValueError。
如果读取不存在的文件:
python
with open("missing.txt", "r", encoding="utf-8") as file:
print(file.read())
会抛出 FileNotFoundError。
异常处理的目标不是掩盖错误,而是让可预期的错误有清楚反馈。
try...except
python
try:
age = int(input("请输入年龄:"))
print(age + 1)
except ValueError:
print("年龄必须输入数字")
如果 try 里的代码出错,并且错误类型是 ValueError,就执行 except。
捕获文件不存在:
python
try:
with open("note.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("文件不存在,请先创建 note.txt")
else 和 finally
else 表示没有异常时执行。
finally 表示不管有没有异常都会执行。
python
try:
number = int(input("请输入数字:"))
except ValueError:
print("输入不是数字")
else:
print(f"你输入的是 {number}")
finally:
print("程序结束")
常见流程:
#mermaid-svg-fYAyaYcioFYwGazB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fYAyaYcioFYwGazB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fYAyaYcioFYwGazB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fYAyaYcioFYwGazB .error-icon{fill:#552222;}#mermaid-svg-fYAyaYcioFYwGazB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fYAyaYcioFYwGazB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fYAyaYcioFYwGazB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fYAyaYcioFYwGazB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fYAyaYcioFYwGazB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fYAyaYcioFYwGazB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fYAyaYcioFYwGazB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fYAyaYcioFYwGazB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fYAyaYcioFYwGazB .marker.cross{stroke:#333333;}#mermaid-svg-fYAyaYcioFYwGazB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fYAyaYcioFYwGazB p{margin:0;}#mermaid-svg-fYAyaYcioFYwGazB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fYAyaYcioFYwGazB .cluster-label text{fill:#333;}#mermaid-svg-fYAyaYcioFYwGazB .cluster-label span{color:#333;}#mermaid-svg-fYAyaYcioFYwGazB .cluster-label span p{background-color:transparent;}#mermaid-svg-fYAyaYcioFYwGazB .label text,#mermaid-svg-fYAyaYcioFYwGazB span{fill:#333;color:#333;}#mermaid-svg-fYAyaYcioFYwGazB .node rect,#mermaid-svg-fYAyaYcioFYwGazB .node circle,#mermaid-svg-fYAyaYcioFYwGazB .node ellipse,#mermaid-svg-fYAyaYcioFYwGazB .node polygon,#mermaid-svg-fYAyaYcioFYwGazB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fYAyaYcioFYwGazB .rough-node .label text,#mermaid-svg-fYAyaYcioFYwGazB .node .label text,#mermaid-svg-fYAyaYcioFYwGazB .image-shape .label,#mermaid-svg-fYAyaYcioFYwGazB .icon-shape .label{text-anchor:middle;}#mermaid-svg-fYAyaYcioFYwGazB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fYAyaYcioFYwGazB .rough-node .label,#mermaid-svg-fYAyaYcioFYwGazB .node .label,#mermaid-svg-fYAyaYcioFYwGazB .image-shape .label,#mermaid-svg-fYAyaYcioFYwGazB .icon-shape .label{text-align:center;}#mermaid-svg-fYAyaYcioFYwGazB .node.clickable{cursor:pointer;}#mermaid-svg-fYAyaYcioFYwGazB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fYAyaYcioFYwGazB .arrowheadPath{fill:#333333;}#mermaid-svg-fYAyaYcioFYwGazB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fYAyaYcioFYwGazB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fYAyaYcioFYwGazB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fYAyaYcioFYwGazB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fYAyaYcioFYwGazB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fYAyaYcioFYwGazB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fYAyaYcioFYwGazB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fYAyaYcioFYwGazB .cluster text{fill:#333;}#mermaid-svg-fYAyaYcioFYwGazB .cluster span{color:#333;}#mermaid-svg-fYAyaYcioFYwGazB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fYAyaYcioFYwGazB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fYAyaYcioFYwGazB rect.text{fill:none;stroke-width:0;}#mermaid-svg-fYAyaYcioFYwGazB .icon-shape,#mermaid-svg-fYAyaYcioFYwGazB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fYAyaYcioFYwGazB .icon-shape p,#mermaid-svg-fYAyaYcioFYwGazB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fYAyaYcioFYwGazB .icon-shape .label rect,#mermaid-svg-fYAyaYcioFYwGazB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fYAyaYcioFYwGazB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fYAyaYcioFYwGazB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fYAyaYcioFYwGazB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
进入 try
是否发生异常?
执行 except
执行 else
执行 finally
不要乱用 except Exception
下面写法能抓很多错误:
python
try:
number = int(input("请输入数字:"))
except Exception:
print("出错了")
但它的问题是太宽。
你不知道到底出了什么错,也可能把真正应该修复的 bug 藏起来。
更好的方式是捕获具体异常:
python
try:
number = int(input("请输入数字:"))
except ValueError:
print("请输入合法数字")
只有当你确实需要兜底处理,并且会记录错误信息时,才考虑捕获更宽的异常。
完整案例,学习记录工具
这个工具会:
- 读取已有学习记录。
- 追加一条新记录。
- 保存回 JSON 文件。
- 文件不存在时自动创建。
python
import json
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
RECORD_FILE = BASE_DIR / "study_records.json"
def load_records() -> list[dict]:
"""读取学习记录,文件不存在时返回空列表。"""
if not RECORD_FILE.exists():
return []
with RECORD_FILE.open("r", encoding="utf-8") as file:
return json.load(file)
def save_records(records: list[dict]) -> None:
"""保存学习记录到 JSON 文件。"""
with RECORD_FILE.open("w", encoding="utf-8") as file:
json.dump(records, file, ensure_ascii=False, indent=2)
name = input("请输入姓名:")
course = input("请输入课程名:")
days = int(input("请输入学习天数:"))
records = load_records()
records.append({
"name": name,
"course": course,
"days": days
})
save_records(records)
print("学习记录已保存")
这个例子已经是一个小工具了。
它不只是打印结果,还能把数据保存下来。
常见错误和修复
FileNotFoundError
原因:文件路径不存在。
修复:
- 检查当前工作目录。
- 使用
Path(__file__).parent定位代码所在目录。 - 读取前判断
path.exists()。
JSONDecodeError
原因:JSON 文件内容格式不合法。
比如少了一个逗号,或者文件是空的。
处理方式:
python
import json
try:
with open("user.json", "r", encoding="utf-8") as file:
user = json.load(file)
except json.JSONDecodeError:
print("JSON 文件格式不正确")
中文乱码
原因:写入和读取编码不一致。
修复:
写入和读取都统一:
python
encoding="utf-8"
数字输入错误
原因:用户输入的不是合法数字。
修复:
python
try:
days = int(input("请输入学习天数:"))
except ValueError:
print("学习天数必须是整数")
练习,做一个通讯录
要求:
- 用户输入姓名和手机号。
- 保存到
contacts.json。 - 支持多次追加联系人。
- 文件不存在时自动创建。
参考代码可以沿用上面的学习记录工具,把字段改成 name 和 phone。
参考资料
- Python 输入输出教程:https://docs.python.org/3/tutorial/inputoutput.html
- Python 异常教程:https://docs.python.org/3/tutorial/errors.html
- Python JSON 文档:https://docs.python.org/3/library/json.html