从零开始学Python(十):实战项目 - 个人记账本
本系列教程将带你从零开始学习Python编程,无需任何编程基础。
项目简介 我们将创建一个个人记账本 应用程序,帮助你记录日常收入和支出,查看余额,并分析消费情况。
功能需求包括添加交易、查看余额、查看历史、数据分析、数据持久化。技术要点包括使用类和对象进行面向对象编程,使用列表和字典存储交易数据,使用文件操作保存和加载数据,使用异常处理处理用户输入错误,使用控制流程实现菜单循环和条件判断。
完整代码 创建文件 account_book.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 import jsonimport osfrom datetime import datetimeclass Transaction : """交易类""" TYPE_INCOME = "收入" TYPE_EXPENSE = "支出" EXPENSE_CATEGORIES = ["餐饮" , "交通" , "购物" , "娱乐" , "居住" , "医疗" , "教育" , "其他" ] INCOME_CATEGORIES = ["工资" , "奖金" , "投资" , "兼职" , "其他" ] def __init__ (self, transaction_type, amount, category, note="" ): """初始化交易""" self .date = datetime.now().strftime("%Y-%m-%d %H:%M:%S" ) self .type = transaction_type self .amount = amount self .category = category self .note = note def to_dict (self ): """转换为字典(用于保存到文件)""" return { "date" : self .date, "type" : self .type , "amount" : self .amount, "category" : self .category, "note" : self .note } @classmethod def from_dict (cls, data ): """从字典创建交易对象(用于从文件加载)""" transaction = cls( data["type" ], data["amount" ], data["category" ], data.get("note" , "" ) ) transaction.date = data["date" ] return transaction def __str__ (self ): """字符串表示""" sign = "+" if self .type == self .TYPE_INCOME else "-" return f"{self.date} | {self.type } | {self.category} | {sign} {self.amount:.2 f} 元 | {self.note} " class AccountBook : """记账本类""" DATA_FILE = "account_data.json" def __init__ (self ): """初始化记账本""" self .transactions = [] self .load_data() def add_transaction (self ): """添加交易""" print ("\n" + "=" * 60 ) print (" 添加交易" ) print ("=" * 60 ) while True : print ("\n请选择交易类型:" ) print ("1. 收入" ) print ("2. 支出" ) choice = input ("请输入选择(1-2):" ).strip() if choice == "1" : trans_type = Transaction.TYPE_INCOME categories = Transaction.INCOME_CATEGORIES break elif choice == "2" : trans_type = Transaction.TYPE_EXPENSE categories = Transaction.EXPENSE_CATEGORIES break else : print ("无效选择,请重试" ) while True : try : amount = float (input ("\n请输入金额:" )) if amount <= 0 : print ("金额必须大于0" ) continue break except ValueError: print ("请输入有效的数字" ) print (f"\n请选择{trans_type} 类别:" ) for i, cat in enumerate (categories, 1 ): print (f"{i} . {cat} " ) while True : try : cat_choice = int (input (f"请选择(1-{len (categories)} ):" )) if 1 <= cat_choice <= len (categories): category = categories[cat_choice - 1 ] break else : print (f"请输入1-{len (categories)} 之间的数字" ) except ValueError: print ("请输入有效的数字" ) note = input ("\n请输入备注(可选,直接回车跳过):" ).strip() transaction = Transaction(trans_type, amount, category, note) self .transactions.append(transaction) print ("\n✅ 交易添加成功!" ) def view_balance (self ): """查看余额""" print ("\n" + "=" * 60 ) print (" 账户余额" ) print ("=" * 60 ) total_income = 0 total_expense = 0 for trans in self .transactions: if trans.type == Transaction.TYPE_INCOME: total_income += trans.amount else : total_expense += trans.amount balance = total_income - total_expense print (f"\n总收入:{total_income:.2 f} 元" ) print (f"总支出:{total_expense:.2 f} 元" ) print (f"当前余额:{balance:.2 f} 元" ) print ("=" * 60 ) def view_history (self ): """查看交易历史""" print ("\n" + "=" * 80 ) print (" 交易历史" ) print ("=" * 80 ) if not self .transactions: print ("\n还没有任何交易记录!" ) print ("=" * 80 ) return for i, trans in enumerate (reversed (self .transactions), 1 ): print (f"{i} . {trans} " ) print ("=" * 80 ) print (f"共 {len (self.transactions)} 条记录" ) def analyze_expenses (self ): """分析支出""" print ("\n" + "=" * 60 ) print (" 支出分析" ) print ("=" * 60 ) expense_by_category = {} for trans in self .transactions: if trans.type == Transaction.TYPE_EXPENSE: category = trans.category if category not in expense_by_category: expense_by_category[category] = 0 expense_by_category[category] += trans.amount if not expense_by_category: print ("\n还没有支出记录!" ) print ("=" * 60 ) return total_expense = sum (expense_by_category.values()) print (f"\n总支出:{total_expense:.2 f} 元" ) print ("\n类别统计:" ) print ("-" * 60 ) sorted_categories = sorted ( expense_by_category.items(), key=lambda x: x[1 ], reverse=True ) for category, amount in sorted_categories: percentage = (amount / total_expense) * 100 bar_length = int (percentage / 2 ) bar = "█" * bar_length print (f"{category:8s} | {amount:8.2 f} 元 | {percentage:5.1 f} % | {bar} " ) print ("-" * 60 ) print (f"最大支出类别:{sorted_categories[0 ][0 ]} " ) print ("=" * 60 ) def save_data (self ): """保存数据到文件""" try : data = [trans.to_dict() for trans in self .transactions] with open (self .DATA_FILE, "w" , encoding="utf-8" ) as file: json.dump(data, file, ensure_ascii=False , indent=2 ) print (f"\n✅ 数据已保存到 {self.DATA_FILE} " ) except Exception as e: print (f"\n❌ 保存数据失败:{e} " ) def load_data (self ): """从文件加载数据""" if not os.path.exists(self .DATA_FILE): return try : with open (self .DATA_FILE, "r" , encoding="utf-8" ) as file: data = json.load(file) self .transactions = [Transaction.from_dict(item) for item in data] print (f"✅ 已加载 {len (self.transactions)} 条交易记录" ) except Exception as e: print (f"❌ 加载数据失败:{e} " ) self .transactions = [] def clear_all_data (self ): """清空所有数据""" confirm = input ("\n⚠️ 确定要清空所有数据吗?此操作不可恢复!(yes/no):" ) if confirm.lower() == "yes" : self .transactions = [] self .save_data() print ("✅ 所有数据已清空" ) else : print ("取消操作" ) def display_menu (self ): """显示菜单""" print ("\n" + "=" * 60 ) print (" 个人记账本" ) print ("=" * 60 ) print ("1. 添加交易" ) print ("2. 查看余额" ) print ("3. 查看历史" ) print ("4. 支出分析" ) print ("5. 保存数据" ) print ("6. 清空数据" ) print ("0. 退出" ) print ("=" * 60 ) def run (self ): """运行主程序""" print ("\n🎉 欢迎使用个人记账本!" ) while True : self .display_menu() choice = input ("\n请选择操作(0-6):" ).strip() if choice == "0" : print ("\n感谢使用,再见!" ) self .save_data() break elif choice == "1" : self .add_transaction() elif choice == "2" : self .view_balance() elif choice == "3" : self .view_history() elif choice == "4" : self .analyze_expenses() elif choice == "5" : self .save_data() elif choice == "6" : self .clear_all_data() else : print ("\n❌ 无效选择,请重试" ) def main (): """主函数""" app = AccountBook() app.run() if __name__ == "__main__" : main()
Transaction类表示单条交易记录,包含日期、类型、金额、类别、备注等属性。to_dict()方法转换为字典便于保存,from_dict()方法从字典创建对象便于加载,str ()方法定义字符串表示。
AccountBook类是记账本主程序管理所有交易。add_transaction()添加新交易,view_balance()查看账户余额,view_history()查看交易历史,analyze_expenses()分析支出,save_data()保存到JSON文件,load_data()从JSON文件加载,run()是主循环。
关键技术点包括使用面向对象编程组织代码,使用JSON格式保存和加载数据,处理用户输入错误和文件操作错误,使用列表存储和遍历交易记录,使用字典统计各类别支出,使用字符串格式化美化输出显示。
你可以继续扩展这个项目,添加搜索功能按日期、类别、关键词搜索交易,预算管理设置月度预算超支提醒,使用matplotlib绘制收支图表,支持CSV格式导入导出,支持多个账户如现金银行卡等,自动添加每月固定收支。
数据保存在account_data.json文件中,位于程序同一目录。直接复制JSON文件即可备份数据。如果没有备份数据无法恢复,建议定期备份。
通过这个项目,我们综合运用了Python基础语法、变量与输入、数据类型、控制流程、函数、数据结构、文件操作、异常处理、面向对象等全部知识点。
恭喜你! 你已经完成了《从零开始学Python》全部10章的学习!
你学会了Python基础语法和数据类型,控制流程和函数,数据结构(列表、字典、元组、集合),文件操作和异常处理,面向对象编程,完整项目开发。
接下来你可以继续扩展记账本项目,学习Python进阶主题如装饰器、生成器等,探索Python库如NumPy、Pandas、Django等,参与开源项目。
学习资源包括Python官方文档、Python教程、LeetCode练习编程题、GitHub查看开源项目。记住:编程是一项实践技能,多写代码才能不断进步!
祝你在Python学习之路上一帆风顺!
系列导航 :