🚀 Python 零基础入门系列(终篇):综合实战项目
《代码之钥》------ 个人记账管理系统(Personal Accounting Manager)
📢 系列说明 :
恭喜你来到本系列的收官之作!在前十三篇中,我们从基础语法到函数,从文件操作到异常处理,你已经掌握了 Python 的"十八般兵器"。
俗话说:"纸上得来终觉浅,绝知此事要躬行。"
本文将带你把前 13 篇学到的所有知识(语法、函数、模块、文件、异常等)融合在一起,从零开始开发一个完整的 "个人记账管理系统"。这不仅是一个项目,更是你迈向 Python 开发者之路的"成人礼"。
📅 更新时间:2026 年 3 月 30 日
🎯 本篇你将学到:项目需求分析、模块化设计、数据持久化、异常处理实战、面向过程的综合应用
⏱️ 预计阅读时间 :60 分钟 | 💻 实践时间:90-120 分钟
📌 前置知识:已完成前十三篇所有内容
✍️ 作者:书到用时方恨少!
🌟 前言:为什么要开发这个项目?
在前十三篇教程中,我们写的代码大多是"碎片化"的。今天,我们要把这些碎片拼成一幅完整的画卷。
记账管理系统是一个经典的 CRUD(增删改查)应用:
- Create (创建):添加一笔收入或支出。
- Read (读取):查看所有账单或按条件查询。
- Update (更新):修改账单信息。
- Delete (删除):删除错误账单。
通过这个项目,你将学会:
- 模块化思维:如何把一个大程序拆分成多个小文件。
- 数据持久化 :利用
JSON模块将内存数据保存到硬盘。 - 防御性编程 :利用
try-except处理用户错误输入。 - 工程化结构:如何组织一个 Python 项目的目录。
1. 项目需求分析
1.1 核心功能
- 启动加载:程序启动时自动从文件加载历史账单。
- 记账功能 :
- 录入类型(收入/支出)。
- 录入金额。
- 录入分类(如餐饮、交通、工资等)。
- 录入日期(默认当前日期)。
- 录入备注。
- 查询功能 :
- 查看所有账单。
- 按月份查询。
- 按分类查询。
- 统计功能:显示当前余额、总收入和总支出。
- 退出保存:程序退出时自动保存数据。
1.2 数据结构设计
我们需要一个结构来存储每一笔"记录":
{
"id": 1, # 唯一标识
"type": "支出", # 收入/支出
"amount": 50.0, # 金额
"category": "餐饮", # 分类
"date": "2026-04-01", # 日期
"note": "午餐" # 备注
}
所有记录将存储在一个列表中,并使用 JSON 格式保存在 accounting.json 文件中。
1.3 技术栈回顾
- 基础语法:变量、数据类型(字典、列表)。
- 流程控制 :
if-else判断,while循环。 - 函数:封装重复代码。
- 模块 :
json模块处理数据,os模块检查文件。 - 异常处理 :
try-except捕获输入错误(如输入非数字金额)。
2. 项目架构设计
为了代码清晰,我们将项目拆分为 4 个模块:
2.1 目录结构
Personal_Accounting/
│
├── data/ # 数据文件夹
│ └── accounting.json # 账单数据
│
├── config.py # 配置文件(常量)
├── file_utils.py # 文件读写模块
├── logic.py # 业务逻辑模块(核心算法)
├── ui.py # 用户界面模块(输入输出)
└── main.py # 主程序入口
2.2 模块划分
config.py:定义常量(如文件路径、记账类型)。file_utils.py:负责数据的加载(Load)和保存(Save)。logic.py:负责数据的增删改查(CRUD)逻辑。ui.py:负责展示菜单、接收用户输入。main.py:负责程序的流程控制(死循环直到用户退出)。
3. 核心代码实现
3.1 数据模型与常量 (config.py)
定义项目中会用到的常量,避免"魔法数字"。
python
# config.py
import os
# --- 基础配置 ---
# 获取当前脚本所在目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 数据文件夹路径
DATA_DIR = os.path.join(BASE_DIR, 'data')
# 账单文件路径
ACCOUNT_FILE = os.path.join(DATA_DIR, 'accounting.json')
# --- 记账配置 ---
# 记账类型
TYPES = ['收入', '支出']
# 常用分类
CATEGORIES = {
'收入': ['工资', '奖金', '理财', '兼职', '其他'],
'支出': ['餐饮', '交通', '购物', '娱乐', '住房', '通讯', '医疗', '学习', '其他']
}
3.2 文件操作模块 (file_utils.py)
利用 json 模块实现数据的持久化,这里要处理文件不存在的异常。
python
# file_utils.py
import json
import os
from config import ACCOUNT_FILE, DATA_DIR
def ensure_data_dir():
"""确保数据目录存在"""
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR)
def load_data():
"""
从文件加载数据
Returns: 账单列表
"""
ensure_data_dir()
# 如果文件不存在,返回空列表
if not os.path.exists(ACCOUNT_FILE):
return []
try:
with open(ACCOUNT_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
# 确保数据是列表格式
return data if isinstance(data, list) else []
except (json.JSONDecodeError, Exception) as e:
print(f"❌ 数据文件读取错误:{e}。将创建新的空账本。")
return []
def save_data(records):
"""
保存数据到文件
Args: records (list): 账单列表
"""
try:
with open(ACCOUNT_FILE, 'w', encoding='utf-8') as f:
json.dump(records, f, ensure_ascii=False, indent=4)
print("✅ 数据已自动保存!")
except Exception as e:
print(f"❌ 数据保存失败:{e}")
3.3 业务逻辑模块 (logic.py)
处理核心数据逻辑,不涉及用户交互。
python
# logic.py
import json
from datetime import datetime
from config import TYPES
def add_record(records, type_, amount, category, note="", date_=None):
"""
添加一条记录
"""
if date_ is None:
date_ = datetime.now().strftime("%Y-%m-%d")
# 生成 ID:取最大 ID + 1,如果没有数据则为 1
new_id = max([r['id'] for r in records] or [0]) + 1
record = {
"id": new_id,
"type": type_,
"amount": amount,
"category": category,
"date": date_,
"note": note
}
records.append(record)
return True
def delete_record(records, record_id):
"""
删除记录
"""
initial_len = len(records)
records[:] = [r for r in records if r['id'] != record_id]
return len(records) < initial_len
def query_records(records, month=None, category=None):
"""
查询记录(支持过滤)
"""
result = records
if month:
# 格式:2026-04
result = [r for r in result if r['date'].startswith(month)]
if category:
result = [r for r in result if r['category'] == category]
return result
def get_statistics(records):
"""
统计数据
"""
total_income = sum(r['amount'] for r in records if r['type'] == '收入')
total_expense = sum(r['amount'] for r in records if r['type'] == '支出')
balance = total_income - total_expense
return {
'total_income': total_income,
'total_expense': total_expense,
'balance': balance
}
3.4 用户交互界面 (ui.py)
负责与用户"对话",这里大量使用 try-except 处理输入错误。
python
# ui.py
from logic import get_statistics
from config import TYPES,CATEGORIES
from file_utils import load_data
def show_menu():
"""显示主菜单"""
print("\n" + "=" * 40)
print("💰 欢迎使用个人记账管理系统")
print("=" * 40)
stats = get_statistics(load_data()) # 这里为了演示,实际应传入内存数据
print(f"📊 当前余额:{stats['balance']:.2f} (收入:{stats['total_income']:.2f} 支出:{stats['total_expense']:.2f})")
print("-" * 40)
print("1. 📝 添加记录")
print("2. 📋 查看所有记录")
print("3. 🔍 按条件查询")
print("4. 🗑️ 删除记录")
print("5. 🚪 退出系统")
print("-" * 40)
def get_user_choice():
"""获取用户选择"""
try:
choice = input("👉 请选择功能 (1-5): ").strip()
return int(choice)
except ValueError:
return -1 # 无效输入
def input_record():
"""交互式输入一条记录"""
while True:
print("\n--- 记账类型 ---")
for i, t in enumerate(TYPES, 1):
print(f"{i}. {t}")
try:
type_idx = int(input("请选择类型 (1-2): ")) - 1
if type_idx not in [0, 1]:
raise ValueError
type_ = TYPES[type_idx]
amount = float(input(f"请输入{type_}金额: "))
if amount <= 0:
raise ValueError("金额必须大于0")
category = input(f"请输入分类 (如{CATEGORIES[type_][0]}): ").strip()
note = input("请输入备注 (可选): ").strip()
return type_, amount, category, note
except ValueError as e:
if "could not convert" in str(e):
print("❌ 错误:请输入有效的数字!")
else:
print(f"❌ 输入错误:{e},请重新输入!")
3.5 主程序入口 (main.py)
项目的"大脑",控制程序的流转。
python
# main.py
"""
Python 零基础入门系列(终篇):综合实战项目
个人记账管理系统主程序
"""
import os
from file_utils import load_data, save_data
from logic import add_record, delete_record, query_records, get_statistics
from ui import show_menu, get_user_choice, input_record
# --- 全局变量 ---
# 在内存中维护的账单列表
records = []
def main():
global records
print("🚀 系统启动中...")
# 1. 加载数据
records = load_data()
print(f"💾 已加载 {len(records)} 条历史数据。")
# 2. 主循环
while True:
show_menu()
choice = get_user_choice()
if choice == 1:
# 添加记录
print("\n--- 添加新记录 ---")
data = input_record()
if data:
type_, amount, category, note = data
if add_record(records, type_, amount, category, note):
print(f"✅ 成功记录:{type_} {amount}元 ({category})")
elif choice == 2:
# 查看所有
print("\n--- 所有账单 ---")
stats = get_statistics(records)
for r in records:
print(f"[{r['date']}] ID:{r['id']} {r['type']} {r['amount']:.2f} ({r['category']}) {r['note']}")
print(f"--- 总计:收入 {stats['total_income']:.2f} | 支出 {stats['total_expense']:.2f} | 余额 {stats['balance']:.2f} ---")
elif choice == 3:
# 查询
month = input("请输入查询月份 (格式 YYYY-MM, 直接回车查询所有): ").strip()
filtered = query_records(records, month=month if month else None)
print(f"\n--- 查询结果 ({len(filtered)} 条) ---")
for r in filtered:
print(f"{r['date']} {r['type']} {r['amount']:.2f}元 ({r['category']})")
elif choice == 4:
# 删除
try:
rid = int(input("请输入要删除的记录ID: "))
if delete_record(records, rid):
print("✅ 删除成功!")
else:
print("❌ 未找到该ID的记录!")
except ValueError:
print("❌ 请输入有效的ID数字!")
elif choice == 5:
# 退出
print("👋 感谢使用,正在保存数据...")
save_data(records)
print("👋 再见!")
break
else:
print("❌ 无效选择,请输入 1-5 之间的数字。")
# 暂停,按回车继续
input("\n⌨️ 按回车键继续...")
if __name__ == "__main__":
main()
4. 运行与测试
-
创建项目文件夹 :新建
Personal_Accounting文件夹。 -
创建文件 :按照上述代码分别创建
config.py,file_utils.py,logic.py,ui.py,main.py。 -
安装依赖:本项目仅使用标准库,无需额外安装。
-
运行 :
cd Personal_Accounting python main.py -
测试点 :
- 启动后是否生成了
data/accounting.json? - 添加几笔收入和支出,退出后数据是否还在?
- 输入非数字的金额,程序是否会崩溃?(应该会提示错误并重新输入)
- 启动后是否生成了
5. 总结与展望
🎉 恭喜你!你已经完成了 Python 零基础入门系列的全部内容!
通过这个《个人记账管理系统》,你不仅复习了:
- 基础语法:变量、循环、判断。
- 数据结构:字典、列表、JSON。
- 函数与模块:代码的封装与复用。
- 异常处理:让程序更健壮。
- 文件操作:数据的持久化。
你现在的身份已经从"Python 学习者"转变为"Python 开发者"!
下一步建议 :
虽然我们完成了基础版,但这个项目还可以继续升级,作为你后续的练手项目:
- 美化界面 :使用
PrettyTable库让表格更美观。 - 增加图表 :使用
matplotlib画出每月支出饼图。 - 数据校验:增加日期格式校验。
- 数据库:尝试将 JSON 换成 SQLite 数据库存储。