本文目录导读:

我来帮你写一个收支记账记录脚本,提供三种不同复杂度的方案,从简单到功能完整。
简易版本(控制台应用)
# simple_accounting.py
import json
from datetime import datetime
import os
class SimpleAccounting:
def __init__(self, filename="records.json"):
self.filename = filename
self.records = self.load_records()
def load_records(self):
if os.path.exists(self.filename):
with open(self.filename, 'r', encoding='utf-8') as f:
return json.load(f)
return []
def save_records(self):
with open(self.filename, 'w', encoding='utf-8') as f:
json.dump(self.records, f, ensure_ascii=False, indent=2)
def add_record(self, type_, amount, category, note=""):
record = {
"id": len(self.records) + 1,
"type": type_, # "收入" or "支出"
"amount": float(amount),
"category": category,
"note": note,
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.records.append(record)
self.save_records()
print(f"✅ 记录成功:{type_} ¥{amount} - {category}")
def show_summary(self):
total_income = sum(r["amount"] for r in self.records if r["type"] == "收入")
total_expense = sum(r["amount"] for r in self.records if r["type"] == "支出")
balance = total_income - total_expense
print(f"\n📊 收支汇总")
print(f"{'─' * 30}")
print(f"💰 总收入:¥{total_income:.2f}")
print(f"💸 总支出:¥{total_expense:.2f}")
print(f"💎 余额: ¥{balance:.2f}")
print(f"📝 总记录:{len(self.records)} 条")
def show_records(self, limit=10):
print(f"\n📋 最近记录(最新{limit}条)")
print(f"{'─' * 60}")
for record in self.records[-limit:]:
print(f"{record['date'][:10]} | {record['type']:^4} | ¥{record['amount']:>8.2f} | {record['category']:^8} | {record['note']}")
def search(self, keyword):
results = [r for r in self.records if keyword in str(r)]
print(f"\n🔍 搜索:'{keyword}' - 找到 {len(results)} 条结果")
for r in results:
print(f"{r['date'][:10]} | {r['type']:^4} | ¥{r['amount']:>8.2f} | {r['category']:^8} | {r['note']}")
def main():
app = SimpleAccounting()
while True:
print(f"\n{'=' * 40}")
print("📒 简易记账本")
print(f"{'=' * 40}")
print("1. 添加收入")
print("2. 添加支出")
print("3. 查看汇总")
print("4. 查看记录")
print("5. 搜索记录")
print("6. 退出")
choice = input("\n请选择操作 (1-6): ").strip()
if choice == "1" or choice == "2":
try:
amount = input("金额: ")
category = input("类别 (工资/兼职/奖金/其他): ")
note = input("备注 (可选): ")
app.add_record("收入" if choice == "1" else "支出", amount, category, note)
except ValueError:
print("❌ 金额输入错误!")
elif choice == "3":
app.show_summary()
elif choice == "4":
app.show_records()
elif choice == "5":
keyword = input("输入搜索关键词: ")
app.search(keyword)
elif choice == "6":
print("👋 再见!")
break
input("\n按回车键继续...")
if __name__ == "__main__":
main()
更多功能的版本
# advanced_accounting.py
import json
import csv
from datetime import datetime, date
import os
from collections import defaultdict
class AccountingSystem:
def __init__(self, filename="accounting_data.json"):
self.filename = filename
self.data = self.load_data()
def load_data(self):
"""加载数据"""
if os.path.exists(self.filename):
with open(self.filename, 'r', encoding='utf-8') as f:
return json.load(f)
return {
"records": [],
"categories": {
"收入": ["工资", "奖金", "兼职", "投资", "红包", "其他"],
"支出": ["餐饮", "交通", "购物", "娱乐", "住房", "医疗", "教育", "其他"]
}
}
def save_data(self):
"""保存数据"""
with open(self.filename, 'w', encoding='utf-8') as f:
json.dump(self.data, f, ensure_ascii=False, indent=2)
def add_record(self, type_, amount, category, note="", date_str=None):
"""添加记录"""
if date_str is None:
date_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
record = {
"id": len(self.data["records"]) + 1,
"type": type_,
"amount": float(amount),
"category": category,
"note": note,
"date": date_str,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.data["records"].append(record)
self.save_data()
return record
def get_statistics(self, start_date=None, end_date=None):
"""获取统计信息"""
records = self.data["records"]
# 如果指定日期范围,过滤记录
if start_date and end_date:
records = [r for r in records if start_date <= r["date"][:10] <= end_date]
# 计算各类统计
total_income = sum(r["amount"] for r in records if r["type"] == "收入")
total_expense = sum(r["amount"] for r in records if r["type"] == "支出")
# 按类别统计
income_by_category = defaultdict(float)
expense_by_category = defaultdict(float)
for r in records:
if r["type"] == "收入":
income_by_category[r["category"]] += r["amount"]
else:
expense_by_category[r["category"]] += r["amount"]
# 按月统计
monthly_stats = defaultdict(lambda: {"收入": 0, "支出": 0})
for r in records:
month = r["date"][:7]
monthly_stats[month][r["type"]] += r["amount"]
return {
"total_records": len(records),
"total_income": total_income,
"total_expense": total_expense,
"balance": total_income - total_expense,
"income_by_category": dict(income_by_category),
"expense_by_category": dict(expense_by_category),
"monthly_stats": dict(monthly_stats)
}
def export_to_csv(self, filename="accounting_export.csv"):
"""导出为CSV"""
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(["ID", "类型", "金额", "类别", "备注", "日期"])
for r in self.data["records"]:
writer.writerow([r["id"], r["type"], r["amount"], r["category"], r["note"], r["date"]])
print(f"✅ 已导出到 {filename}")
def show_detailed_report(self):
"""显示详细报表"""
stats = self.get_statistics()
print(f"\n{'=' * 50}")
print("📊 详细财务报表")
print(f"{'=' * 50}")
print(f"📍 统计周期:全部")
print(f"📝 总记录数:{stats['total_records']}")
print(f"💰 总收入:¥{stats['total_income']:.2f}")
print(f"💸 总支出:¥{stats['total_expense']:.2f}")
print(f"💎 结余:¥{stats['balance']:.2f}")
print(f"\n📈 收入分类明细:")
print(f"{'─' * 40}")
for cat, amount in sorted(stats['income_by_category'].items(), key=lambda x: x[1], reverse=True):
percentage = (amount / stats['total_income'] * 100) if stats['total_income'] > 0 else 0
print(f" {cat:^8}: ¥{amount:>8.2f} ({percentage:.1f}%)")
print(f"\n📉 支出分类明细:")
print(f"{'─' * 40}")
for cat, amount in sorted(stats['expense_by_category'].items(), key=lambda x: x[1], reverse=True):
percentage = (amount / stats['total_expense'] * 100) if stats['total_expense'] > 0 else 0
print(f" {cat:^8}: ¥{amount:>8.2f} ({percentage:.1f}%)")
print(f"\n📅 月度收支趋势:")
print(f"{'─' * 40}")
for month in sorted(stats['monthly_stats'].keys()):
m_stats = stats['monthly_stats'][month]
balance = m_stats['收入'] - m_stats['支出']
print(f" {month}: 收入¥{m_stats['收入']:>8.2f} | 支出¥{m_stats['支出']:>8.2f} | 结余¥{balance:>8.2f}")
def main():
app = AccountingSystem()
while True:
print(f"\n{'=' * 50}")
print("📒 智能记账系统")
print(f"{'=' * 50}")
print("1. 📝 添加收入记录")
print("2. 📝 添加支出记录")
print("3. 📊 查看财务报表")
print("4. 📋 查看最近记录")
print("5. 🔍 搜索记录")
print("6. 📤 导出CSV")
print("7. ⚙️ 管理类别")
print("8. 🗑️ 删除记录")
print("0. 🚪 退出")
choice = input("\n请选择操作 (0-8): ").strip()
if choice == "1":
try:
amount = float(input("💰 金额: "))
print("类别:")
for i, cat in enumerate(app.data["categories"]["收入"], 1):
print(f" {i}. {cat}")
cat_choice = int(input("选择类别编号: ")) - 1
category = app.data["categories"]["收入"][cat_choice]
note = input("📝 备注 (可选): ")
app.add_record("收入", amount, category, note)
print("✅ 收入记录成功!")
except (ValueError, IndexError) as e:
print(f"❌ 输入错误:{e}")
elif choice == "2":
try:
amount = float(input("💰 金额: "))
print("类别:")
for i, cat in enumerate(app.data["categories"]["支出"], 1):
print(f" {i}. {cat}")
cat_choice = int(input("选择类别编号: ")) - 1
category = app.data["categories"]["支出"][cat_choice]
note = input("📝 备注 (可选): ")
app.add_record("支出", amount, category, note)
print("✅ 支出记录成功!")
except (ValueError, IndexError) as e:
print(f"❌ 输入错误:{e}")
elif choice == "3":
app.show_detailed_report()
elif choice == "4":
limit = input("显示多少条?(默认10): ")
limit = int(limit) if limit.strip() else 10
for r in app.data["records"][-limit:]:
print(f"{r['date'][:10]} | {r['type']:^4} | ¥{r['amount']:>8.2f} | {r['category']:^8} | {r['note']}")
elif choice == "5":
keyword = input("🔍 搜索关键词: ")
results = [r for r in app.data["records"] if keyword in str(r)]
print(f"找到 {len(results)} 条结果:")
for r in results:
print(f"{r['date'][:10]} | {r['type']:^4} | ¥{r['amount']:>8.2f} | {r['category']:^8} | {r['note']}")
elif choice == "6":
app.export_to_csv()
elif choice == "7":
print("类别管理功能开发中...")
elif choice == "8":
try:
id_to_delete = int(input("输入要删除的记录ID: "))
app.data["records"] = [r for r in app.data["records"] if r["id"] != id_to_delete]
app.save_data()
print(f"✅ 已删除记录 #{id_to_delete}")
except ValueError:
print("❌ 无效的ID")
elif choice == "0":
print("👋 感谢使用,再见!")
break
input("\n按回车键继续...")
if __name__ == "__main__":
main()
带GUI界面的版本(使用tkinter)
如果你想要图形界面,这里是一个简单的GUI版本:
# gui_accounting.py
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
import json
import os
class AccountingApp:
def __init__(self, root):
self.root = root
self.root.title("简易记账本")
self.root.geometry("800x600")
self.filename = "records.json"
self.records = self.load_records()
self.setup_ui()
def load_records(self):
if os.path.exists(self.filename):
with open(self.filename, 'r', encoding='utf-8') as f:
return json.load(f)
return []
def save_records(self):
with open(self.filename, 'w', encoding='utf-8') as f:
json.dump(self.records, f, ensure_ascii=False, indent=2)
def setup_ui(self):
# 创建主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 输入区域
input_frame = ttk.LabelFrame(main_frame, text="添加记录", padding="10")
input_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
# 金额输入
ttk.Label(input_frame, text="金额:").grid(row=0, column=0, sticky=tk.W)
self.amount_var = tk.StringVar()
ttk.Entry(input_frame, textvariable=self.amount_var, width=15).grid(row=0, column=1, padx=5)
# 类型选择
ttk.Label(input_frame, text="类型:").grid(row=0, column=2, sticky=tk.W)
self.type_var = tk.StringVar(value="支出")
ttk.Combobox(input_frame, textvariable=self.type_var, values=["收入", "支出"], width=10, state="readonly").grid(row=0, column=3, padx=5)
# 类别选择
ttk.Label(input_frame, text="类别:").grid(row=0, column=4, sticky=tk.W)
self.category_var = tk.StringVar()
category_combo = ttk.Combobox(input_frame, textvariable=self.category_var, width=15, state="readonly")
category_combo.grid(row=0, column=5, padx=5)
# 根据类型更新类别
def update_categories(*args):
if self.type_var.get() == "收入":
category_combo['values'] = ["工资", "奖金", "兼职", "投资", "红包", "其他"]
else:
category_combo['values'] = ["餐饮", "交通", "购物", "娱乐", "住房", "医疗", "教育", "其他"]
self.category_var.set("")
self.type_var.trace('w', update_categories)
# 备注输入
ttk.Label(input_frame, text="备注:").grid(row=0, column=6, sticky=tk.W)
self.note_var = tk.StringVar()
ttk.Entry(input_frame, textvariable=self.note_var, width=20).grid(row=0, column=7, padx=5)
# 添加按钮
ttk.Button(input_frame, text="添加记录", command=self.add_record).grid(row=0, column=8, padx=5)
# 统计信息区域
stats_frame = ttk.LabelFrame(main_frame, text="统计信息", padding="10")
stats_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
self.stats_labels = {}
stats_info = [("总收入", "total_income"), ("总支出", "total_expense"), ("结余", "balance")]
for i, (label, key) in enumerate(stats_info):
ttk.Label(stats_frame, text=f"{label}:").grid(row=0, column=i*2, padx=10)
self.stats_labels[key] = ttk.Label(stats_frame, text="¥0.00", font=("Arial", 10, "bold"))
self.stats_labels[key].grid(row=0, column=i*2+1, padx=5)
# 记录列表
list_frame = ttk.LabelFrame(main_frame, text="收支记录", padding="10")
list_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
# 创建表格
columns = ("日期", "类型", "金额", "类别", "备注")
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=15)
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=100)
self.tree.column("日期", width=120)
self.tree.column("金额", width=100)
self.tree.pack(fill=tk.BOTH, expand=True)
# 滚动条
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.configure(yscrollcommand=scrollbar.set)
# 操作按钮
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=3, column=0, columnspan=2, pady=10)
ttk.Button(button_frame, text="删除选中", command=self.delete_record).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="刷新列表", command=self.refresh_display).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="导出数据", command=self.export_data).pack(side=tk.LEFT, padx=5)
# 初始化显示
self.refresh_display()
# 配置grid权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
main_frame.rowconfigure(2, weight=1)
def add_record(self):
try:
amount = float(self.amount_var.get())
type_ = self.type_var.get()
category = self.category_var.get()
note = self.note_var.get()
if amount <= 0:
messagebox.showerror("错误", "金额必须大于0")
return
if not category:
messagebox.showerror("错误", "请选择类别")
return
record = {
"id": len(self.records) + 1,
"type": type_,
"amount": amount,
"category": category,
"note": note,
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.records.append(record)
self.save_records()
self.refresh_display()
# 清空输入
self.amount_var.set("")
self.category_var.set("")
self.note_var.set("")
messagebox.showinfo("成功", f"{type_} ¥{amount:.2f} 记录成功!")
except ValueError:
messagebox.showerror("错误", "请输入有效的金额")
def refresh_display(self):
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
# 计算统计
total_income = sum(r["amount"] for r in self.records if r["type"] == "收入")
total_expense = sum(r["amount"] for r in self.records if r["type"] == "支出")
balance = total_income - total_expense
# 更新统计信息
self.stats_labels["total_income"].config(text=f"¥{total_income:.2f}")
self.stats_labels["total_expense"].config(text=f"¥{total_expense:.2f}")
self.stats_labels["balance"].config(text=f"¥{balance:.2f}")
# 添加记录到表格
for record in reversed(self.records[-100:]): # 显示最近100条
self.tree.insert("", "end", values=(
record["date"][:16],
record["type"],
f"¥{record['amount']:.2f}",
record["category"],
record["note"]
))
def delete_record(self):
selected = self.tree.selection()
if not selected:
messagebox.showwarning("警告", "请选择要删除的记录")
return
if messagebox.askyesno("确认", "确定删除选中的记录吗?"):
# 获取选中的记录索引
for item in selected:
values = self.tree.item(item)["values"]
# 查找匹配的记录并删除
for record in self.records[:]:
if (record["date"][:16] == values[0] and
record["type"] == values[1] and
f"¥{record['amount']:.2f}" == values[2]):
self.records.remove(record)
break
self.save_records()
self.refresh_display()
messagebox.showinfo("成功", "记录已删除")
def export_data(self):
if not self.records:
messagebox.showwarning("警告", "没有可导出的数据")
return
filename = f"accounting_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.records, f, ensure_ascii=False, indent=2)
messagebox.showinfo("成功", f"数据已导出到 {filename}")
if __name__ == "__main__":
root = tk.Tk()
app = AccountingApp(root)
root.mainloop()
使用建议
-
选择哪个版本?
- 如果只是简单用用,方案一就够
- 想要更多功能,用方案二
- 想要图形界面,用方案三
-
数据存储
- 所有方案都使用JSON文件存储
- 可以手动编辑JSON文件
- 建议定期备份数据文件
-
扩展功能
- 可以添加数据库支持(SQLite)
- 可以添加图表显示
- 可以添加预算管理功能
- 可以添加报表导出功能
-
运行方式
- 保存为
.py文件后直接运行 - 方案三需要安装tkinter(Python自带)
- 保存为
需要我帮你进一步定制某个版本,或者添加特定功能吗?