AI编程(二十二):简易记账本 — 月底看看钱都去哪了

作者:

在

文件存账目、列表管记录、函数做统计——第十二篇价格工具的”理财升级版”

一、月底的灵魂拷问

每个月底总有那么一个瞬间:看看银行卡余额,倒吸一口凉气,然后问出那个经典问题——

“我钱呢??”

明明没买什么大件,钱就是没了。吃饭、打车、奶茶、网购……每笔都不大,加起来吓人。

解药只有一个:记账。不用记很久,认真记一个月,你就能看清自己的消费习惯。手机记账App很多,但今天我们要自己写一个——因为自己写的能完全按自己的习惯来,而且会用到一堆重要知识:文件读写(账目要持久保存)、列表+字典(每条账目是字典,所有账目是列表)、统计函数(分类汇总、月度汇总)、日期处理(datetime)、循环+条件(老朋友)。

这一篇做完,你手里会有个真正能天天用的工具。

二、设计:一条账目长什么样

先想数据结构。一条账目至少要有:

  • 金额:花了多少(数字)
  • 类别:吃饭、交通、购物、娱乐……(文字)
  • 备注:中午麻辣烫(文字)
  • 日期:什么时候花的(文字,如”2025-01-15″)

这正好是一个字典:

{"金额": 25.5, "类别": "吃饭", "备注": "中午麻辣烫", "日期": "2025-01-15"}

而所有账目,就是装着这种字典的列表:

账目 = [
    {"金额": 25.5, "类别": "吃饭", "备注": "中午麻辣烫", "日期": "2025-01-15"},
    {"金额": 6.0,  "类别": "交通", "备注": "地铁", "日期": "2025-01-15"},
    ...
]

列表装字典——第十七篇成绩管理器、第二十一篇计时器列表都是这个套路,你已经很熟了。

三、先跟AI聊

你的提示词:

用Python写一个命令行记账本。功能:1.记录支出(输入金额、类别、备注,日期自动取今天);2.查看所有账目;3.显示总支出;4.把账目保存到JSON文件、启动时自动加载。用列表存账目,每条账目是字典,用函数实现各功能,主循环显示菜单。中文界面,详细注释。

四、基础版代码

import json
from datetime import datetime

文件名 = "我的账本.json"
账目 = []          # 所有账目:列表装字典

# ========== 文件操作 ==========
def 加载账目():
    """启动时读取之前的账目"""
    global 账目
    try:
        with open(文件名, "r", encoding="utf-8") as f:
            账目 = json.load(f)
        print(f"📂 已加载 {len(账目)} 条账目")
    except FileNotFoundError:
        print("📭 还没有账本,从今天开始记吧!")

def 保存账目():
    """把账目写入文件"""
    with open(文件名, "w", encoding="utf-8") as f:
        json.dump(账目, f, ensure_ascii=False, indent=2)
    print(f"💾 已保存 {len(账目)} 条账目")

# ========== 记一笔 ==========
def 记一笔():
    """添加一条支出记录"""
    备注 = input("备注(买了什么):").strip()
    if not 备注:
        print("❌ 备注不能为空")
        return

    try:
        金额 = float(input("金额(元):"))
    except ValueError:
        print("❌ 请输入数字")
        return
    if 金额 <= 0:
        print("❌ 金额要大于0")
        return

    类别 = input("类别(吃饭/交通/购物/娱乐/其他):").strip()
    if not 类别:
        类别 = "其他"

    # 日期自动取今天
    今天 = datetime.now().strftime("%Y-%m-%d")

    # 组装成字典,加进列表
    新账目 = {"金额": 金额, "类别": 类别, "备注": 备注, "日期": 今天}
    账目.append(新账目)
    print(f"✅ 已记录:{今天} {类别} {备注} {金额}元")

# ========== 看账本 ==========
def 查看全部():
    """显示所有账目"""
    if not 账目:
        print("📭 账本是空的")
        return

    print(f"n{'日期':<12}{'类别':<8}{'备注':8}")
    print("-" * 44)
    for 条 in 账目:
        print(f"{条['日期']:<12}{条['类别']:<8}{条['备注']:7.2f}元")
    print("-" * 44)
    print(f"共 {len(账目)} 条记录")

# ========== 总支出 ==========
def 算总账():
    """算出总支出"""
    总额 = 0
    for 条 in 账目:
        总额 += 条["金额"]        # 累加每一条的金额
    print(f"n💰 总支出:{总额:.2f}元")
    if 账目:
        print(f"📊 平均每笔:{总额 / len(账目):.2f}元")

# ========== 主菜单 ==========
def 主菜单():
    加载账目()
    while True:
        print("n" + "=" * 28)
        print("  📒 我的记账本")
        print("=" * 28)
        print("  1. 记一笔")
        print("  2. 查看账本")
        print("  3. 看总支出")
        print("  4. 保存")
        print("  0. 退出")
        选择 = input("请选择:").strip()

        if 选择 == "1":
            记一笔()
        elif 选择 == "2":
            查看全部()
        elif 选择 == "3":
            算总账()
        elif 选择 == "4":
            保存账目()
        elif 选择 == "0":
            保存账目()          # 退出前自动保存
            print("👋 再见!")
            break
        else:
            print("❌ 无效选择")

主菜单()

五、逐行理解(只讲新面孔)

大部分代码你都见过——第十二篇价格工具的结构几乎一模一样。这里只讲三个值得注意的点:

5.1 自动日期

今天 = datetime.now().strftime("%Y-%m-%d")
  • datetime.now():现在的日期和时间(第十二篇用过)
  • .strftime("%Y-%m-%d"):把时间格式化成2025-01-15这种样子。%Y年、%m月、%d日

记账时日期自动填今天,不用手输。能自动的绝不让用户输——这是好工具的基本修养。

5.2 累加求和

总额 = 0
for 条 in 账目:
    总额 += 条["金额"]

经典的”累加器”模式:准备一个变量存总数(从0开始),循环里逐条加上去。第六篇学循环时就见过这个套路,现在用在真实场景里。

5.3 对齐输出

print(f"{条['备注']:7.2f}元")

<14左对齐占14格,>7.2f右对齐占7格保留2位小数。文字左对齐数字右对齐,表格才整齐——Excel里也是这个规矩。

六、运行试试

先记几天账,然后选2查看:

日期        类别    备注            金额
--------------------------------------------
2025-01-15  吃饭    中午麻辣烫       25.50元
2025-01-15  交通    地铁             6.00元
2025-01-15  购物    洗发水           39.90元
2025-01-16  吃饭    早餐包子          5.00元
--------------------------------------------
共 4 条记录

选3:💰 总支出:76.40元。

关掉程序再打开——账目还在(JSON文件记得住)。工具可以正式启用了。

但用几天就会想:光看总支出没用啊,钱到底花在哪一类了?——改进一来解决。

七、第一个改进:按类别统计

想知道”这个月吃饭花了多少、购物花了多少”。思路:准备一个汇总字典,类别做键、金额累加做值,遍历所有账目往里填:

def 按类别统计():
    """统计每个类别花了多少钱"""
    if not 账目:
        print("📭 账本是空的")
        return

    汇总 = {}                # {类别: 该类别总金额}
    for 条 in 账目:
        类别 = 条["类别"]
        金额 = 条["金额"]
        if 类别 in 汇总:
            汇总[类别] += 金额     # 已有此类别,累加
        else:
            汇总[类别] = 金额      # 第一次见,新建一项

    # 算出总支出,用于计算占比
    总额 = 0
    for 金额 in 汇总.values():
        总额 += 金额

    # 按金额从大到小排序后显示
    print(f"n📊 消费分类统计(总支出{总额:.2f}元)")
    print("-" * 40)

    # sorted排序:按字典的值(金额)降序
    排序后 = sorted(汇总.items(), key=lambda x: x[1], reverse=True)
    for 类别, 金额 in 排序后:
        占比 = 金额 / 总额 * 100
        print(f"  {类别:8.2f}元  占比{占比:>5.1f}%")

新知识点:sorted排序

排序后 = sorted(汇总.items(), key=lambda x: x[1], reverse=True)

这行是本篇最”高级”的一行,拆开看:

  • 汇总.items():把字典变成”键值对”的列表,比如[("吃饭", 156.5), ("交通", 42.0), ...]
  • sorted(...):给这些对儿排序
  • key=lambda x: x[1]:按什么排?x代表每个键值对,x[0]是类别、x[1]是金额。x[1]的意思是”按金额排”
  • reverse=True:从大到小

lambda是个匿名小函数——”临时工函数”,只在这一行干活,随用随写不用起名字。第一次见可能会愣一下,但多看两眼就习惯了:lambda x: x[1]就是”给我一个东西,我还你它的第二个元素”。

效果:

📊 消费分类统计(总支出76.40元)
----------------------------------------
  购物      39.90元  占比 52.2%
  吃饭      30.50元  占比 39.9%
  交通       6.00元  占比  7.9%

一眼看清:购物占了一半多!这就是统计的意义——数据会告诉你真相。

八、第二个改进:月度汇总

记了一个月,月底想看”这个月花了多少、每天平均多少、最贵的一笔是什么”。加一个月度报告函数:

def 月度汇总():
    """按月汇总账目"""
    if not 账目:
        print("📭 账本是空的")
        return

    月份 = input("看哪个月(如2025-01,直接回车看全部):").strip()

    # 筛选出该月的账目
    if 月份:
        本月账目 = [条 for 条 in 账目 if 条["日期"].startswith(月份)]
    else:
        本月账目 = 账目

    if not 本月账目:
        print(f"📭 {月份}没有账目")
        return

    # === 各项统计 ===
    总额 = 0
    最大额 = 0
    最大备注 = ""
    按天 = {}              # {"2025-01-15": 当天总额}

    for 条 in 本月账目:
        总额 += 条["金额"]
        if 条["金额"] > 最大额:
            最大额 = 条["金额"]
            最大备注 = 条["备注"]
        按天[条["日期"]] = 按天.get(条["日期"], 0) + 条["金额"]

    天数 = len(按天)

    # === 输出报告 ===
    print(f"n📋 {月份 or '全部'} 消费报告")
    print("=" * 36)
    print(f"  总支出:{总额:.2f}元")
    print(f"  消费天数:{天数}天")
    print(f"  日均消费:{总额 / 天数:.2f}元")
    print(f"  最大一笔:{最大额:.2f}元({最大备注})")
    print(f"n  每日明细:")
    for 日期, 当天额 in sorted(按天.items()):
        print(f"    {日期}  {当天额:>8.2f}元")

新知识点:startswith

本月账目 = [条 for 条 in 账目 if 条["日期"].startswith(月份)]
  • startswith():字符串”以……开头”的判断。"2025-01-15".startswith("2025-01")是True。筛1月份的账,就是筛日期以”2025-01″开头的。
  • 列表推导式:[条 for 条 in 账目 if 条件]——”从账目里把符合条件的挑出来,装成新列表”。第十二篇搜索功能用过同款。

新知识点:按天分组

按天[条["日期"]] = 按天.get(条["日期"], 0) + 条["金额"]

这行是”分组累加”的经典写法,值得背下来:

  • 按天.get(日期, 0):取字典里这一天的总额,取不到就当0(.get的第二个参数是默认值)
  • 取出来加上本笔金额,再存回去

第一次遇到1月15日:get返回0,加上25.5,存25.5。又一笔15日的6元:get返回25.5,加6,存31.5。分组统计就是这么算的——第八篇字典+第六篇循环的组合拳。

最大值追踪

if 条["金额"] > 最大额:
    最大额 = 条["金额"]
    最大备注 = 条["备注"]

打擂台模式:开始时擂主是0,每来一条账目就比一比,比擂主大就换擂主。循环结束,留在台上的就是最大值。这也是最值问题的标准解法。

九、第三个改进:预算提醒 — 花超了喊停

光看统计还不够,得防患于未然:设个月度预算,快超了就提醒。

预算文件 = "预算.txt"

def 设置预算():
    """设置月度预算"""
    try:
        金额 = float(input("每月预算(元):"))
    except ValueError:
        print("❌ 请输入数字")
        return
    if 金额 = 100:
        print(f"n🚨 警报!本月已花 {总额:.2f}元,超出预算 {总额 - 预算:.2f}元!")
    elif 比例 >= 80:
        print(f"n⚠️ 注意!本月已花 {总额:.2f}元,用掉预算的{比例:.0f}%")
    elif 比例 >= 50:
        print(f"n📌 提醒:本月已花预算的{比例:.0f}%")

然后在”记一笔”成功之后,顺手调用检查预算()——每记一笔就顺便看一眼还剩多少额度,超了当场被警告。把提醒嵌进使用流程里,比单独查一次有效得多。

分级提醒用的是嵌套的条件判断:超100%红色警报、超80%黄色注意、超50%温和提醒。第五篇学的if/elif,用在这里就是”用户心理”:不同紧张程度给不同强度的信号。

预算存在一个单独的小文件里(就一个数字,不用JSON那么隆重)。数据存哪里、用什么格式,要看数据本身的复杂度——这也是经验。

十、完整功能清单

我的记账本
├── 记一笔(金额/类别/备注,日期自动)
├── 查看账本(对齐表格)
├── 总支出 + 平均每笔
├── 按类别统计(排序+占比)
├── 月度汇总(日均/最大一笔/每日明细)
├── 预算提醒(50%/80%/100%三级)
└── 自动保存/加载(JSON文件)

从第十二篇的价格记录工具,到今天的记账本——结构相似,但功能深了一大截:多了统计、排序、分组、预算。同样的知识,用得越熟,能做的东西越复杂。

十一、回顾一下用到了什么

知识点 在哪里用的

|——–|———–|

列表装字典 账目数据结构
append 记一笔
字典 每条账目、分组汇总
字典.get 分组累加、默认值0
字典.values() 算总额
字典.items() 遍历分类统计
sorted+lambda 分类按金额排序
列表推导式 筛选某月账目
startswith 判断日期属于哪个月
累加器模式 总额、分类汇总
打擂台模式 找最大一笔
文件操作 JSON账目、TXT预算
global 加载时修改全局列表
try/except 金额输入、文件不存在
datetime 自动日期、当前月份
strftime 日期格式化
函数 每个功能一个函数
f-string对齐 表格输出

十二、今日小练习

练习1:支出排行榜

写一个函数,找出”最费钱的3笔支出”。提示:用sorted按金额降序排,取前3个(切片[:3],第八篇学过)。

练习2:本周统计

加一个”本周账目”功能:算出今天是本周的第几天(提示:datetime.now().weekday(),周一=0周日=6),往前推算出本周一的日期,筛选本周的账目并汇总。这题有点难,日期推算容易绕晕,建议先自己画个草图再问AI。

练习3:收入支持

现在只能记支出。加上收入:账目字典里加一个”类型”字段(支出/收入),总支出和总收入分开算,月度汇总里显示”结余=收入-支出”。想想哪些函数要改?


下一篇:番茄钟计时器 — 学习25分钟、休息5分钟。把第二十一篇的倒计时升级成一个完整的工作法工具,还能统计你今天完成了几个番茄。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注