本篇成果:一个命令行待办清单。
添加、查看、完成、删除,数据存进文件,关掉终端重开还在。含注释120行左右,五脏俱全,没有一行多余。
太长不看版(给想直接看代码的你)
| 项目信息 | 一句话说明 |
|---|---|
| 项目名称 | 命令行待办清单(todo cli) |
| 代码行数 | ~120行(含注释) |
| 依赖 | 零依赖,全部python自带模块 |
| 核心功能 | 增删改查 + json持久化 |
| 跑起来的命令 | python main.py add "写一篇笔记" |
| 技术点 | argparse、json、pathlib、strftime |
| 做完你能得到 | 第一个“关掉终端再开数据还在”的程序 |
一、成品预览:先看我们要做什么
$ 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"""
import json
import argparse
from pathlib import path
from time import strftime
data_file = path("todos.json") # 数据文件就放项目根目录
def load_todos():
"""读数据;第一次使用(文件不存在)返回空列表"""
if data_file.exists():
return json.loads(data_file.read_text(encoding="utf-8"))
return []
def save_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行以内
添加
def add_todo(text):
todos = load_todos()
todos.append({"text": text, "done": false,
"created": strftime("%y-%m-%d %h:%m")})
save_todos(todos)
print(f"已添加:{text}")
查看列表
def list_todos():
todos = load_todos()
if not todos:
print("清单是空的,来一条:python main.py add 学python")
return
for i, t in enumerate(todos, start=1):
flag = "✅" if t["done"] else "⬜"
print(f"{i}. {flag} {t['text']}({t['created']})")
done_count = sum(1 for t in todos if t["done"])
print(f"—— 已完成 {done_count}/{len(todos)} ——")
标记完成
def complete_todo(index):
todos = load_todos()
try:
t = todos[index - 1] # 用户编号从1开始,列表下标从0开始
except indexerror:
print(f"没有第 {index} 条,先用list看看")
return
t["done"] = true
save_todos(todos)
print(f"完成:{t['text']}")
删除
def delete_todo(index):
todos = load_todos()
try:
t = todos.pop(index - 1)
except indexerror:
print(f"没有第 {index} 条")
return
save_todos(todos)
print(f"已删除:{t['text']}")
注意每个函数都是同一个节奏
读 → 改 → 存
这个“读改存”循环就是所有数据类应用的原子结构——从待办清单到银行系统,骨架不变,变的只是规模。
index - 1是新手的经典坑(编号从1开始给人看,下标从0开始给程序用),第9节③专门讲它。
七、第4步:argparse接上命令行
最后一块:让程序听懂add、list这些指令。
def main():
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()
if args.command == "add":
add_todo(args.text)
elif args.command == "list":
list_todos()
elif args.command == "done":
complete_todo(args.index)
elif args.command == "del":
delete_todo(args.index)
if __name__ == "__main__":
main()
两个关键点
| 知识点 | 说明 |
|---|---|
sub.add_parser | 给程序装“子命令”:add、list各管各的 |
type=int | argparse自动把用户输入转成整数,转换失败会替你报错 |
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篇流程存档:
git add . git commit -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.py add "去超市买牛奶" # ✅ 正确 python main.py add 去超市买牛奶 # ❌ 只存了"去"
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) |
到此这篇关于基于python编写一个命令行待办清单的实战教学的文章就介绍到这了,更多相关python命令行待办清单内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论