Python 毕业项目:个人记账与统计 CLI 工具的完整工程化
Python 模块出口项目:把 start 模块的记账原型升级为正式工程——argparse 子命令、JSON 与 SQLite 双存储、异常处理、pytest 单元测试、logging、pyproject.toml 打包、README 与 Git 管理,五个里程碑从高提示走到无提示,附可逐条勾选的验收断言。
前置知识
- 已走完 Python 模块主线,至少完成到 Python 与 CI/CD(该篇只影响选做的 CI 一条,标注可后补,不阻塞开工);
- 手里有 start 模块的记账原型:一个
ledger.py,交互式录入、JSON 存文件、退出时打印汇总。原型代码可以丢,里面的历史账目数据留着,本文会把它迁进新工程。
背景与目标
start/070 的记账程序 60 行代码解决了「记下账、不丢失」,但它是个原型:单文件、交互式 while 循环、逻辑与输入输出搅在一起、没有测试、不能安装。它能证明你会 Python,却不能证明你能交付软件。
本项目把这份原型升级为正式工程:一个名叫 ledger 的命令行工具,装进环境后在任何目录敲 ledger 就能用,带子命令、双存储后端、单元测试、日志与打包配置。做完后它不是「一份代码」,而是一个可发布、可复现、可继续生长的作品——与本库 FANDEX 仓库 走的是同一条工程链:需求 → 实现 → 测试 → 打包 → README → Git 管理。
学完本项目你新增的能力:
- 把脚本感程序重构为包结构,业务逻辑与命令行界面分离;
- 用 argparse 设计子命令接口,用自定义异常把崩溃变成体面的报错;
- 面向存储接口编程,JSON 与 SQLite 两套后端随时切换;
- 用 pytest 建立安全网,让每次重构都有底气;
- 用 pyproject.toml 打包、写像样的 README、用 Git 管理全程。
预计两天(对应 Level 6 到 7)。项目名、命令名、数据文件位置、SQLite 表结构、统计口径与输出格式均由你自己决定——这是你的作品,不是抄写题。
User stories(必做 12 条)
每条都是可检查的断言。示例命令假定工具名是 ledger,换成你起的名字同理。
- P1 可安装:
pip install -e .成功后,在任意目录运行ledger --help能打印用法——工具是安装出来的,不是python xxx.py跑出来的。 - P2 一条命令记一笔:
ledger add 早饭 12.5写入一条账目;执行ledger add 早饭 abc时以非 0 退出码结束,stderr 有可读错误、全程无 Traceback 字样。 - P3 查询可用:
ledger list列出全部账目,各列对齐可读;ledger list输出的条数与实际存储条数一致。 - P4 双存储后端:默认用 JSON 文件存储;加参数(如
--db sqlite)切换 SQLite 后,add 与 list 行为完全不变——两套实现共用同一存储接口,命令层无感。 - P5 模块化:代码是包结构,命令层、存储层、统计层各自独立文件;统计与存储函数不依赖
input与print,可以被import后直接调用。 - P6 统计正确:
ledger stats至少输出三项——总支出、最大单笔、按用途小计;手工用几条已知账目核对,数字正确。 - P7 崩溃变报错:把数据文件手工改成非法 JSON 后运行
ledger list,程序以非 0 退出码结束、提示文件损坏或如何恢复,而非抛出 Traceback。 - P8 测试安全网:
pytest -q全绿,测试条数不少于 10,其中统计层与存储层各至少 2 条;全套测试 5 秒内跑完(不碰网络、不依赖真实数据文件)。 - P9 日志克制:加
--verbose时输出 DEBUG 级日志(记录每次读写与关键分支);默认运行没有任何日志噪音;实现用 logging 模块,不是 print。 - P10 打包规范:
pyproject.toml声明了项目名、版本与依赖;本项目可以只依赖标准库——依赖声明哪怕为空也要写清楚。 - P11 仓库纪律:Git 仓库就位,
.gitignore排除__pycache__/、.pytest_cache/、dist/;提交信息符合约定式格式(参照 Git 毕业项目 的 A4)。 - P12 README 可复现:README 至少含「这是什么」「安装」「用法」「示例输出」「如何运行测试」五段;照 README 从 clone 到跑通不超过 5 分钟。
Extra credit(选做 3 条)
- E1 pipx 可安装:
pipx install你的 wheel 后,全局任何目录ledger --help可用(工具安装见 030 篇)。 - E2 月度统计:
ledger stats --month 2026-09只统计当月账目,跨月数据不被计入;用两条不同月份的账目验证。 - E3 CI 全绿:GitHub Actions 在每次 push 时自动跑 pytest 并显示通过徽章(780 篇,可后补)。
里程碑拆解(5 步)
提示逐级递减:前两步告诉你读哪篇的哪一节,最后一步只给验收断言。
里程碑 1:从原型到包(对应 P5、P11)
高提示:读 模块、包与工程化 的包结构一节与 函数详解 的拆分原则——把 60 行原型按「命令层、存储层、统计层」切开;同时 git init、写最小 .gitignore,让仓库与代码一起生长。
完成后应看到:python -m 包名 仍能完成原型记账的全部功能;历史 JSON 数据迁入新位置后不丢;git log --oneline 已有第一条提交。
里程碑 2:argparse 子命令(对应 P2、P3、P6)
高提示:读 Python 与 CLI 的 argparse 一节,用 subparsers 搭 add、list、stats 三条子命令;while 交互循环退役,录入方式从「问一句答一句」变成一条命令。
完成后应看到:
ledger add 早饭 12.5
ledger list
ledger stats三条命令各自产出正确结果;ledger --help 一屏说清全部用法。
里程碑 3:存储接口与异常(对应 P4、P7)
中提示:读 文件 I/O 的 json 一节与 异常处理 的自定义异常一节。JSON 用标准库 json,SQLite 用标准库 sqlite3——表结构、存储接口长什么样,你自己设计;SQLite 的进阶 ORM 路线见 830 篇,本项目不需要。
完成后应看到:两种后端下 add、list、stats 结果一致;对坏文件运行时退出码非 0 且无 Traceback。这一步起,每个存储函数先写两条 pytest 再写实现(750 篇的节奏)。
里程碑 4:测试与日志(对应 P8、P9)
低提示:只指路——pytest 的组织方式读 Python 与测试 入门小节,logging 的基本配置读 Python 与日志 前两节;测什么、测几条,按 P8 的断言自己列。
完成后应看到:
pytest -q末行全绿且条数达标;ledger --verbose 下日志逐条出现,去掉参数后恢复安静。
里程碑 5:打包、README 与打版(对应 P1、P10、P12)
无提示。验收断言:pip install -e . 后任意目录 ledger --help 可用;pyproject.toml 三要素齐备;README 五段俱全且新人 5 分钟可复现;pytest -q 依然全绿;git status 干净、历史合规、打上 v1.0.0 附注标签并推送远端。所有卡壳点都在上一节链接矩阵里。
提示区:功能到文档的对照矩阵
| 卡在哪 | 去哪篇找答案 |
|---|---|
包结构、python -m 运行 | /python/720-ModulePackageEngineering |
| 函数拆分、默认参数、docstring | /python/100-FunctionDetailed |
| argparse 与子命令 | /python/810-PythonCLI |
| JSON 读写、with 语句 | /python/300-FileIOContextManager |
| 自定义异常、退出码 | /python/130-ExceptionHandling |
| pytest 组织与断言 | /python/750-PythonTest |
| logging 基本配置 | /python/430-PythonLog |
| pyproject.toml 与构建 | /python/730-PythonPackagingEvolution |
| pipx 与 uv 安装工具 | /python/030-PyenvUvManage |
| SQLite 进阶(ORM) | /python/830-PythonSQLAlchemy |
| 代码风格与 Ruff | /python/770-PythonCodeQuality |
| Git 仓库纪律、打标 | /git/430-GitCapstoneProject |
两条通用提示:测试断言优先于实现(先写「stats 对已知数据必须输出什么」,再写实现);存储层测试用临时目录与临时文件,绝不读写你的真实账本。
验收清单
- P1
pip install -e .后任意目录ledger --help可用 - P2 add 正常,非法金额非 0 退出且无 Traceback
- P3 list 对齐完整,条数与存储一致
- P4 JSON 与 SQLite 两后端行为一致
- P5 包结构分层,业务函数可 import 复用
- P6 stats 三项数字与手工核对一致
- P7 坏文件不崩溃,给出恢复提示
- P8 pytest 全绿不少于 10 条,5 秒内
- P9 —verbose 有 DEBUG 日志,默认安静
- P10 pyproject.toml 三要素齐备
- P11 .gitignore 到位,提交信息合规
- P12 README 五段俱全,5 分钟可复现
- E1 pipx 全局可用(选做)
- E2 —month 月度统计正确(选做)
- E3 CI 徽章绿(选做)
常见弯路
- 功能蔓延:导出 CSV、多币种、图表、GUI……每一样都在诱你偏航。主线 12 条验收完之前不加任何功能,想法记进 README 的「路线图」一节;
- 跳过测试:攒到里程碑 4 再补,重构成本已翻倍。从存储层开始测试先行,是本项目最便宜的一张保险;
- 没有 README:仓库不可复现等于没完成。「代码写完了」不是完成标志,「照 README 五分钟能装能用能测」才是;
- 舍不得交互原型:原型的使命已经完成。
add子命令取代 while 循环是升级而非倒退,历史数据手工迁一份即可; - 依赖越装越多:本项目零第三方依赖也能满分,标准库就是「自带电池」;每加一个依赖前先问「标准库真的做不到吗」;
- 边写边弃:卡住时回到提示矩阵按篇找答案,而不是推倒重来——毕业项目练的就是在既有工程上继续前进。
完成后你能做什么
- 任何「脚本感」程序,你都有能力把它升级为可安装、可测试、可发布的正式工具——这套动作与 FANDEX 的工程链完全同构;
- 你拥有了一个可继续生长的真实作品:接数据库迁移(800 篇)、接 FastAPI 把账本暴露成接口(880 篇)、接爬虫自动导账(970 篇),都是现成的续集;
- 在 Python 后端与 AI 路线 上,这个项目就是你简历里的第一个工程作品——下一步无论是 Web API 还是数据方向,起点都是它。