把学过的东西拼成一个能用的东西
目标:一个命令行记账本。能记一笔支出、能看全部记录、能看总额,关掉程序数据还在。
用到的全部知识你都学过了:字典、列表、函数、循环、if、文件、JSON、异常。真实项目就是这么来的——没有新魔法,只是组合。
先想清楚数据长什么样
这是写代码前最该花时间的一步。一笔账需要:花了多少、花在哪、什么时候。
{"amount": 25.5, "category": "吃饭", "date": "2026-03-01"}
多笔账就是一个列表装一堆这样的字典:
[
{"amount": 25.5, "category": "吃饭", "date": "2026-03-01"},
{"amount": 8.0, "category": "交通", "date": "2026-03-01"}
]
数据结构定对了,代码自然就顺了。 这句话你现在可能没感觉,写完这个项目会有。
完整代码
import json
import os
from datetime import date
DATA_FILE = "bills.json"
def load_bills():
"""从文件读出全部账目;文件不存在就返回空列表。"""
if not os.path.exists(DATA_FILE):
return []
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_bills(bills):
"""把全部账目写回文件。"""
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(bills, f, ensure_ascii=False, indent=2)
def add_bill(bills):
try:
amount = float(input("金额:"))
except ValueError:
print("金额得是数字,这笔没记上")
return
category = input("分类(吃饭/交通/购物…):")
bills.append({
"amount": amount,
"category": category,
"date": str(date.today()),
})
save_bills(bills)
print("记好了")
def show_bills(bills):
if not bills:
print("还没有任何记录")
return
for i, b in enumerate(bills, start=1):
print(f'{i}. {b["date"]} {b["category"]} {b["amount"]:.2f} 元')
def show_summary(bills):
total = sum(b["amount"] for b in bills)
print(f"总支出:{total:.2f} 元")
by_category = {}
for b in bills:
by_category[b["category"]] = by_category.get(b["category"], 0) + b["amount"]
for name, amount in sorted(by_category.items(), key=lambda kv: -kv[1]):
print(f" {name}: {amount:.2f} 元")
def main():
bills = load_bills()
while True:
print("\n1 记一笔 2 看记录 3 看统计 4 退出")
choice = input("选择:").strip()
if choice == "1":
add_bill(bills)
elif choice == "2":
show_bills(bills)
elif choice == "3":
show_summary(bills)
elif choice == "4":
print("再见")
break
else:
print("没这个选项")
main()
里面有三个值得你停下来看的写法
1. enumerate(bills, start=1) —— 同时拿下标和元素,并让下标从 1 开始。给人看的编号从 1 开始,程序内部从 0 开始,这是两回事。
2. sum(b["amount"] for b in bills) —— 这叫生成器表达式,等价于"把每个 b 的 amount 挑出来再求和"。展开写是四行循环,写成一行更清楚。
3. by_category.get(name, 0) + amount —— 分组统计的标准套路。第一次遇到某个分类时 get 返回默认值 0,于是不用提前判断"这个键在不在"。
自己动手改进它
按难度递增,挑着做:
- 加一个"删除某笔记录"的功能(提示:
bills.pop(i)然后save_bills) - 统计只看本月(提示:比较
b["date"][:7]和str(date.today())[:7]) - 金额不允许是负数或 0,不合法就提示重输
- 把
main()换成if __name__ == "__main__": main()—— 去问 AI 私教这行是什么意思,它是 Python 项目的标准写法
10 秒自测
如果删掉 save_bills(bills) 那一行,程序还能正常记账吗?
答案是:当次能,重开就全没了。内存里的列表更新了,但没写回文件。这就是"持久化"的意义。