Python 文件读写、JSON 与异常处理,让程序从课堂练习变成可用工具

前面几篇文章里的程序,大多有一个共同问题。

数据只存在内存里。

程序一关,用户输入过什么,计算出什么结果,全都没了。

真实工具不可能这样。它需要读取文件、保存数据、处理错误,遇到问题时给出明确提示,而不是直接崩溃。

这篇文章讲三个很实用的能力:

  1. 文件读写。
  2. JSON 数据保存。
  3. 异常处理。

学完之后,你就能写一个能保存数据的小工具。

文件路径先讲清楚

文件读写最容易出问题的不是语法,而是路径。

看这行:

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(),也不容易忘。

readreadlinereadlines 怎么选

一次性读取全部内容:

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 文本

dumpdumpsloadloads

这四个函数名字很像,但规则很简单。

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")

elsefinally

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("请输入合法数字")

只有当你确实需要兜底处理,并且会记录错误信息时,才考虑捕获更宽的异常。

完整案例,学习记录工具

这个工具会:

  1. 读取已有学习记录。
  2. 追加一条新记录。
  3. 保存回 JSON 文件。
  4. 文件不存在时自动创建。
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

原因:文件路径不存在。

修复:

  1. 检查当前工作目录。
  2. 使用 Path(__file__).parent 定位代码所在目录。
  3. 读取前判断 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("学习天数必须是整数")

练习,做一个通讯录

要求:

  1. 用户输入姓名和手机号。
  2. 保存到 contacts.json
  3. 支持多次追加联系人。
  4. 文件不存在时自动创建。

参考代码可以沿用上面的学习记录工具,把字段改成 namephone

参考资料