news 2026/9/4 6:02:58

【Python项目实战】项目·命令行待办清单:文件读写 + JSON持久化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Python项目实战】项目·命令行待办清单:文件读写 + JSON持久化

🎯本篇成果:一个命令行待办清单。

添加、查看、完成、删除,数据存进文件,关掉终端重开还在。含注释120行左右,五脏俱全,没有一行多余。


📌 太长不看版(给想直接看代码的你)

项目信息一句话说明
项目名称命令行待办清单(Todo CLI)
代码行数~120行(含注释)
依赖零依赖,全部Python自带模块
核心功能增删改查 + JSON持久化
跑起来的命令python main.py add "写一篇笔记"
技术点argparsejsonpathlibstrftime
做完你能得到第一个“关掉终端再开数据还在”的程序

一、成品预览:先看我们要做什么

$ python main.py add 给专栏写一篇笔记 已添加:给专栏写一篇笔记 $ python main.py add 复习第3篇的venv命令 已添加:复习第3篇的venv命令 $ python main.py list 1. ⬜ 给专栏写一篇笔记(2026-09-01 20:00) 2. ⬜ 复习第3篇的venv命令(2026-09-01 20:01) —— 已完成 0/2 —— $ python main.py done 1 完成:给专栏写一篇笔记 $ python main.py list 1. ✅ 给专栏写一篇笔记(2026-09-01 20:00) 2. ⬜ 复习第3篇的venv命令(2026-09-01 20:01) —— 已完成 1/2 —— $ python main.py del 2 已删除:复习第3篇的venv命令 $ python main.py list 1. ✅ 给专栏写一篇笔记(2026-09-01 20:00) —— 已完成 1/1 ——

🚀关键卖点只有一个:关掉终端,明天再开,list一敲,数据还在。

这叫持久化——之前你写的一切脚本都是“运行即失忆”,从今天起你的程序有了记忆。


🤔 为什么选待办清单当第一个项目?

理由说明
功能小一晚上跟得完,不劝退
覆盖全增删改查 + 文件读写 + 命令行参数 = 企业级应用的骨架
零依赖全部用Python自带模块,不装任何第三方包
能复用学完这个,后面FastAPI版的待办清单(第23篇)你会看到同一个需求怎么“长大”

二、前置知识:两分钟补两块砖

🔑 JSON:人能读懂的数据格式

就是长这样的文本文件,和Python的列表/字典几乎一一对应

[{"text":"给专栏写一篇笔记","done":true,"created":"2026-09-01 20:00"},{"text":"复习第3篇的venv命令","done":false,"created":"2026-09-01 20:01"}]
函数作用
json.loads(文本)JSON文本 → Python对象
json.dumps(对象)Python对象 → JSON文本

💡 两个注意:JSON的列表对应[]、字典对应{}ensure_ascii=False参数能让中文原样保存,而不是变成\u4e2d\u6587

📁 pathlib:现代的文件操作

第5篇见过的Path,本篇用到三个技能:

用法作用
Path("todos.json")定位文件(在当前目录)
.exists()判断文件是否存在
.read_text()/.write_text()读写文本内容

💡 有了这两块砖,开工!


三、第0步:3分钟项目setup(前5篇的复习)

按第5篇模板建项目,一气呵成

1. 新建文件夹 todo_project,VSCode打开(File → Open Folder) 2. python -m venv venv # 零依赖也建venv,习惯要从第一个项目养成 3. 激活:windows: venv\Scripts\activate(cmd) | .\venv\Scripts\Activate.ps1(PowerShell) mac: source venv/bin/activate 4. git init,写好.gitignore(venv/ 和 __pycache__/) 5. 新建空文件:main.py

🤔 有同学问:为什么不分utils/故意的。第一版先把它写“歪”,后面复盘时我们亲手把它拆开重构——到时候你会真正明白“为什么要拆”,而不是背口诀。


四、第1步:设计数据结构(写代码前先想清楚)

一条待办长什么样?三个字段:内容、状态、创建时间。整个清单就是这样的列表

# 概念示意,不用敲[{"text":"给专栏写一篇笔记","done":false,"created":"2026-09-01 20:00"},{"text":"复习第3篇的venv命令","done":false,"created":"2026-09-01 20:01"}]

🎯先定结构再动手,是和"想到哪写到哪"的第一个分水岭。之后所有函数都围绕这个结构工作——结构即契约,第9节报错⑤会让你见识破坏契约的后果。


五、第2步:存与读——程序的记忆

"""todo —— 命令行待办清单 v1"""importjsonimportargparsefrompathlibimportPathfromtimeimportstrftime DATA_FILE=Path("todos.json")# 数据文件就放项目根目录defload_todos():"""读数据;第一次使用(文件不存在)返回空列表"""ifDATA_FILE.exists():returnjson.loads(DATA_FILE.read_text(encoding="utf-8"))return[]defsave_todos(todos):"""把列表写回文件;ensure_ascii=False让中文原样保存"""text=json.dumps(todos,ensure_ascii=False,indent=2)DATA_FILE.write_text(text,encoding="utf-8")

📌 两个细节

细节为什么重要
encoding="utf-8"Windows默认编码是GBK,不加=中文乱码(第2篇的老规矩)
indent=2把文件排版成带缩进的人类可读格式——你可以用VSCode直接打开看

六、第3步:四个功能,每个10行以内

添加

defadd_todo(text):todos=load_todos()todos.append({"text":text,"done":False,"created":strftime("%Y-%m-%d %H:%M")})save_todos(todos)print(f"已添加:{text}")

查看列表

deflist_todos():todos=load_todos()ifnottodos:print("清单是空的,来一条:python main.py add 学Python")returnfori,tinenumerate(todos,start=1):flag="✅"ift["done"]else"⬜"print(f"{i}.{flag}{t['text']}{t['created']})")done_count=sum(1fortintodosift["done"])print(f"—— 已完成{done_count}/{len(todos)}——")

标记完成

defcomplete_todo(index):todos=load_todos()try:t=todos[index-1]# 用户编号从1开始,列表下标从0开始exceptIndexError:print(f"没有第{index}条,先用list看看")returnt["done"]=Truesave_todos(todos)print(f"完成:{t['text']}")

删除

defdelete_todo(index):todos=load_todos()try:t=todos.pop(index-1)exceptIndexError:print(f"没有第{index}条")returnsave_todos(todos)print(f"已删除:{t['text']}")

📌 注意每个函数都是同一个节奏

读 → 改 → 存

🎯 这个“读改存”循环就是所有数据类应用的原子结构——从待办清单到银行系统,骨架不变,变的只是规模。

⚠️index - 1是新手的经典坑(编号从1开始给人看,下标从0开始给程序用),第9节③专门讲它。


七、第4步:argparse接上命令行

最后一块:让程序听懂addlist这些指令。

defmain():parser=argparse.ArgumentParser(description="命令行待办清单")sub=parser.add_subparsers(dest="command",required=True)p=sub.add_parser("add",help="添加一条待办")p.add_argument("text",help="待办内容(带空格请加引号)")sub.add_parser("list",help="查看全部待办")p=sub.add_parser("done",help="标记完成")p.add_argument("index",type=int,help="编号,见list输出")p=sub.add_parser("del",help="删除待办")p.add_argument("index",type=int,help="编号,见list输出")args=parser.parse_args()ifargs.command=="add":add_todo(args.text)elifargs.command=="list":list_todos()elifargs.command=="done":complete_todo(args.index)elifargs.command=="del":delete_todo(args.index)if__name__=="__main__":main()

📌 两个关键点

知识点说明
sub.add_parser给程序装“子命令”:addlist各管各的
type=intargparse自动把用户输入转成整数,转换失败会替你报错
if __name__ == "__main__"第5篇说的“入口仪式”,直接运行才执行

八、第5步:验收清单

保存main.py,然后逐条跑

python main.py add 给专栏写一篇笔记 python main.py add 复习第3篇的venv命令 python main.py list python main.py done 1 python main.py list python main.py del 2 python main.py list

✅ 最终验收

关掉终端,重开,再list一次——数据还在,持久化验收通过!

用VSCode打开todos.json看一眼,这就是你程序的“记忆”长什么样。


📦 提交Git

验收通过,按第4篇流程存档:

gitadd.gitcommit-m"完成待办清单v1:增删改查 + JSON持久化"

🎉 你的GitHub上从此多了一个完整项目


九、常见报错:这6个,都是新手第一次写项目的真实坑(重点!)

① 打印✅时报UnicodeEncodeError: 'gbk' codec can't encode character

🔍 原因:老式Windows终端默认GBK编码,装不下emoji。

✅ 解法(二选一):

  • 终端执行chcp 65001切UTF-8(第6篇⑤的回旋镖);
  • 把代码里的✅⬜换成[√][ ],一劳永逸。

json.decoder.JSONDecodeError: Expecting value

🔍 原因todos.json的内容不是合法JSON——多半是你手动编辑时删了个逗号/括号,或者文件被改成了空文件。

✅ 解法:打开文件找语法错(少逗号、多括号);救不回来就删掉文件重新来,你的程序会自动重建。

💡 顺便体会:数据文件是程序的命根,改它要像改代码一样谨慎。


③ done的编号总是差一位

🔍 原因:你在自己的代码里写了todos[index]而不是todos[index - 1]。界面上给用户看的编号从1开始,Python列表下标从0开始——这叫off-by-one错误,程序员的百年老坑。

✅ 解法:定位这一行的最好办法——用第6篇的断点,看看index和实际点到的元素差在哪。


add 去超市存进去只剩“去”

🔍 原因:终端把空格当成参数分隔符,超市被切开了。

✅ 解法:带空格的内容加引号

python main.pyadd"去超市买牛奶"# ✅ 正确python main.pyadd去超市买牛奶# ❌ 只存了"去"

💡 Windows的cmd/PowerShell都认双引号。


KeyError: 'done'

🔍 原因:你手动编辑todos.json时删了某个字段。程序依赖的结构被破坏了——还记得第4节说的“结构即契约”吗。

✅ 解法:把字段补回去;以后想改数据结构,改代码和改数据要同步


⑥ 只敲了python main.py add,蹦出一堆红字

报错内容:

error: the following arguments are required: text

🔍 原因这不是崩溃,是argparse在拦你——你少传了参数,它把正确用法(usage)都打印出来了。

✅ 解法:照着usage提示补上参数。

💡学会读argparse的自动提示,它写的说明书比你想象的细。


十、课后练习(做了才算会)

#练习难度提示
1加一个clear命令,一键删除所有已完成⭐⭐列表推导式[t for t in todos if not t["done"]]+sub.add_parser
2给“完成”加时间戳:done时记done_at,list里已完成项显示完成时间⭐⭐⭐修改complete_todo函数,加字段
3按流程把本项目推上GitHub,按模板补README⭐⭐你的第一个“完整项目”,简历上可以写的那种
4(选做)把统计行改成进度条:[####------] 已完成2/5⭐⭐⭐"#" * n + "-" * (5-n)

📦 配套代码

完整main.py、示例数据与README已上传GitHub(07-todo-cli/todo_project):【https://gitee.com/xqshadow/python-project-in-practice】

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 6:02:13

从零配置代码生成工具链:环境搭建、认证与验证全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 6:02:10

从代码重构到架构优化:如何识别并重构软件中的设计债务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 6:01:24

YOLOv5批量推理优化:从单图到多图并行的吞吐量提升实战

一张图12ms,100张图却要5秒?你的GPU可能在“摸鱼”!本文基于2026年最新实测数据,深入剖析YOLOv5批量推理的优化全链路——从动态批处理到TensorRT深度调优,从多GPU并行到服务化部署,手把手带你将推理吞吐量提升5-10倍。 一、问题的真相:你的GPU利用率为什么只有35%? 2…

作者头像 李华
网站建设 2026/9/4 6:00:13

零基础部署 OpenClaw,图形化安装避开 Python/Node 环境坑

OpenClaw 一键安装包|图形化一键部署,告别复杂环境配置 适配系统:Windows10/11 64 位、macOS 12 当前版本:Windows v3.1.0|macOS v2.7.9 OpenClaw 提供图形化一键部署方案,全程可视化交互,不需要…

作者头像 李华
网站建设 2026/9/4 5:59:51

从光流到RIFE:视频补帧技术原理与动作视频实战指南

“补帧”到底解决了什么问题?先问一个很多视频爱好者和 UP 主都遇到过的问题:你下载到一段 30fps 的舞蹈视频,画面里人物动作很快,比如劈叉、旋转、踢腿,逐帧看的时候总觉得跳跃感明显;你想做慢动作回放&am…

作者头像 李华