零基础入门python07:让程序记住数据——JSON文件和异常处理

零基础入门python07:让程序记住数据------JSON文件和异常处理

一、上一篇课后练习讲解

结算器练习可以使用普通 assert:断言原价、折扣价和负配送费异常。断言表达"程序员确信必须成立的规则",而用户输入错误应该用 try/except 给出友好提示。

可执行验收答案

把测试保存为 test_bill.py,直接用 Python 运行即可:

python 复制代码
from decimal import Decimal
from bill_calculator import calculate_order

assert calculate_order(Decimal("50"), 2, Decimal("5"), "normal")["total"] == Decimal("105")

"应该抛异常"的调用不能直接放在 assert 后面。正确写法是:

python 复制代码
try:
    calculate_order(Decimal("-1"), 1, Decimal("0"), "normal")
except ValueError as exc:
    assert "价格" in str(exc)
else:
    raise AssertionError("负价格没有被拒绝")
print("bill validation passed")

运行 python test_bill.py 应输出 bill validation passed;若把 except 写成裸 except,会把编程错误也吞掉,排查会更困难。

二、本篇要完成什么

把个人账目保存到 records.json,下次启动还能读取;文件损坏或金额输入错误时,程序不崩溃,并说明问题。

三、完整实现

python 复制代码
from pathlib import Path
import json

DATA_FILE = Path('records.json')


def load_records() -> list[dict]:
    """读取持久化数据;文件不存在返回空列表,格式损坏则明确报错。"""
    if not DATA_FILE.exists():
        return []
    try:
        return json.loads(DATA_FILE.read_text(encoding='utf-8'))
    except json.JSONDecodeError as error:
        raise ValueError(f'账本文件不是合法JSON:{error}') from error


def save_records(records: list[dict]) -> None:
    """统一使用UTF-8和缩进保存,便于人工查看和版本控制。"""
    DATA_FILE.write_text(json.dumps(records, ensure_ascii=False, indent=2), encoding='utf-8')


def add_record(records: list[dict], amount: float, note: str) -> None:
    if amount <= 0:
        raise ValueError('金额必须大于0')
    records.append({'amount': amount, 'note': note.strip()})


try:
    records = load_records()
    amount = float(input('本次支出金额:'))
    add_record(records, amount, input('备注:'))
    save_records(records)
    print(f'已保存,共{len(records)}条记录')
except (ValueError, OSError) as error:
    print(f'操作失败:{error}')

Path 负责路径,json 负责格式,业务函数负责金额规则。异常捕获只包住可能失败的操作,不用一个巨大的 except Exception 把真正的编程错误隐藏掉。

四、运行结果和验收

第一次运行会创建 records.json;再次运行会在原列表后追加记录。手工把文件改成 {bad,程序应输出"不是合法JSON",而不是打印 traceback。课后练习:增加收入/支出 kind 字段,并在下一篇用类封装账本对象。

实战补充:JSON 记账本的读写边界

文件项目的核心不是 json.loads,而是定义文件不存在、内容损坏和保存失败时的行为。数据层负责读写,命令行层负责提示。

python 复制代码
def load_records(path: Path) -> list[dict]:
    if not path.exists():
        return []
    try:
        data = json.loads(path.read_text(encoding='utf-8'))
    except json.JSONDecodeError as exc:
        raise ValueError('账本文件不是有效 JSON') from exc
    if not isinstance(data, list):
        raise ValueError('账本根节点必须是列表')
    return data

def save_records(path: Path, records: list[dict]) -> None:
    temp = path.with_suffix('.tmp')
    temp.write_text(json.dumps(records, ensure_ascii=False, indent=2), encoding='utf-8')
    temp.replace(path)

先写临时文件再替换,避免程序写到一半被终止而留下半个 JSON。金额读取后转换 Decimal,保存时再转成字符串,避免浮点污染。

验收与课后练习

首次运行创建空账本,写入两条记录,故意破坏 JSON 后得到可读错误。课后增加导出 CSV,下一篇用类封装账本生命周期。

本篇结束:完整模块文件

本节不是代码片段,而是本篇结束时该模块的完整版本。请先备份旧文件,再整体替换;替换后重新运行本篇命令和测试。阅读时重点看本篇新增的函数、事务边界和错误处理,未涉及的代码先不要自行删减。

main.py

python 复制代码
from pathlib import Path
import json
path=Path('records.json')
try: records=json.loads(path.read_text('utf-8')) if path.exists() else []
except json.JSONDecodeError: records=[]
try:
    amount=float(input('支出金额:'))
    if amount<=0: raise ValueError('金额必须大于0')
    records.append({'amount':amount,'note':input('备注:')})
    path.write_text(json.dumps(records,ensure_ascii=False,indent=2),'utf-8')
except ValueError as error: print('输入错误:',error)
相关推荐
盼小辉丶5 分钟前
PyTorch强化学习实战——融合人类示范数据的高效强化学习
人工智能·pytorch·python·深度学习·强化学习
葡萄城技术团队10 分钟前
工业数据可视化:使用活字格 AIcoding + Three.js 构建轻量化三维设备监控大屏
开发语言·javascript·信息可视化
易点云徐源12 分钟前
(已解决)BOSS直聘 Windows 客户端一直“加载中,请稍候”怎么办?
windows
qiaosaifei14 分钟前
JAVA老项目中有大量的类名_编译时报找不到符号错误
java·开发语言
聪明蛋子哟18 分钟前
MCP协议深度落地:如何用Python/Java双语言实现统一的工具调用网关?
java·开发语言·python
绮罗郁金香31 分钟前
windows延迟更新
windows
李妍.32 分钟前
PyTorch GPU 版安装记录(RTX 5060 + Python 3.14)
人工智能·pytorch·python
90后的晨仔36 分钟前
Python 基础语法完整汇总
python
广慈新医知38 分钟前
当健康问题越来越复杂,AI更适合先帮我们“把问题问清楚”
人工智能·python
一晌小贪欢39 分钟前
python-第27天:Python面向对象详解
开发语言·python·数据可视化·面向对象·python办公