Python进阶-day21:复习与小项目
off999 2025-05-21 15:46 128 浏览 0 评论
学习目标
- 复习内容:巩固OOP、异常处理、文件操作、模块化等知识。
- 高级概念: 设计模式:单例模式(确保账户唯一性)、工厂模式(创建交易对象)。 上下文管理:管理文件操作和数据库连接。 元编程:动态添加方法或属性。 多线程:异步保存数据和统计分析。
- 小项目:开发一个功能强大的命令行个人财务管理系统,支持持久化、并发操作和动态扩展。
- 实践技能:通过项目综合应用高级Python技术,提升代码设计能力。
时间分配(总计:5-7小时)
- 1小时:复习本周内容
- 4-5小时:开发个人财务管理系统项目(融入高级概念)
- 1小时:项目测试、优化与总结
- 你已掌握Python基础、OOP、文件操作、模块化编程。
- 本周学习了OOP、异常处理、文件操作、模块化、数据结构等。
- 熟悉标准库(如json、datetime、os)。
一、复习内容(1小时)
复习重点
- 面向对象编程(OOP): 类、继承、封装、多态 私有属性、属性装饰器(@property)
- 异常处理: try-except-else-finally 自定义异常类
- 文件操作: 读写文本、JSON、CSV with语句管理资源
- 模块与包: 模块导入、包组织 init.py与相对导入
- 数据结构: 列表、字典、集合 推导式、高阶函数(map、filter)
复习方法
- 快速回顾笔记(20分钟):
- 复习笔记或代码,重点关注OOP、异常处理、文件操作。
- 写下每个知识点的核心代码示例。例如:python
- # OOP 示例:使用属性装饰器 class Account: def __init__(self): self._balance = 0 @property def balance(self): return self._balance @balance.setter def balance(self, value): if value < 0: raise ValueError("Balance cannot be negative") self._balance = value
- 练习题(30分钟):
- 完成以下练习: 定义一个Transaction类,使用@property管理金额属性,确保金额为正。 编写一个函数,从transactions.json读取交易记录,处理文件不存在的异常。 创建一个模块utils.py,实现一个高阶函数,过滤金额大于某个阈值的交易。 使用try-except处理用户输入,验证日期格式(YYYY-MM-DD)。
- 总结问题(10分钟):
- 记录复习中的薄弱点,留待项目中实践。
二、小项目:命令行个人财务管理系统(4-5小时)
项目概述
开发一个命令行个人财务管理系统,支持:
- 记录收入和支出(金额、类别、日期、备注)。
- 查看账户余额、交易历史。
- 按类别或日期筛选交易。
- 统计某类别的总收入/支出。
- 数据持久化(JSON文件)。
- 并发保存数据和统计分析。
融入高级概念
- 设计模式: 单例模式:确保全局只有一个Account实例。 工厂模式:通过工厂类创建不同类型的交易(收入、支出)。
- 上下文管理: 使用contextlib管理文件操作,确保资源正确关闭。 实现自定义上下文管理器,记录操作日志。
- 元编程: 使用metaclass动态为Transaction类添加审计方法。 使用setattr动态添加统计方法。
- 多线程: 使用threading异步保存数据到文件。 使用线程池执行统计分析。
项目功能需求
- 主菜单: 添加收入/支出 查看余额、交易历史 筛选交易(按类别或日期) 统计分析 退出并保存
- 数据存储: 使用JSON文件存储交易和余额。 异步保存数据。
- 输入验证: 验证金额、日期、类别。
- 统计功能: 异步计算类别统计。
- 日志: 使用上下文管理器记录操作日志。
项目结构
personal_finance/
├── main.py # 主程序入口
├── account.py # 账户和交易逻辑(单例模式、工厂模式、元编程)
├── storage.py # 文件操作(上下文管理、多线程)
├── menu.py # 用户交互界面
├── logger.py # 日志管理(上下文管理)
└── data.json # 数据存储文件
项目实现步骤
1. 日志管理(logger.py)
实现上下文管理器,记录操作日志。
python
# logger.py
from contextlib import contextmanager
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
filename="finance.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
@contextmanager
def operation_logger(operation_name):
"""上下文管理器,记录操作开始和结束"""
logging.info(f"Starting operation: {operation_name}")
start_time = datetime.now()
try:
yield
logging.info(f"Completed operation: {operation_name}, Duration: {datetime.now() - start_time}")
except Exception as e:
logging.error(f"Failed operation: {operation_name}, Error: {str(e)}")
raise
2. 账户与交易逻辑(account.py)
实现单例模式、工厂模式和元编程。
python
# account.py
from datetime import datetime
import uuid
from abc import ABC, ABCMeta, abstractmethod
from logger import operation_logger # Import operation_logger
class TransactionMeta(ABCMeta):
"""Custom metaclass for Transaction, inheriting from ABCMeta to resolve metaclass conflict"""
def __new__(cls, name, bases, attrs):
# Dynamically add an audit method to the class
def audit(self):
return f"Audit: Transaction {self.id} created at {self.created_at}"
attrs['audit'] = audit
return super().__new__(cls, name, bases, attrs)
class Transaction(ABC, metaclass=TransactionMeta):
"""Abstract base class for transactions"""
def __init__(self, amount, category, date=None, note=""):
if amount <= 0:
raise ValueError("Amount must be positive")
self.id = str(uuid.uuid4())[:8] # Unique transaction ID
self.amount = amount
self.category = category
self.date = date or datetime.now().strftime("%Y-%m-%d")
self.note = note
self.created_at = datetime.now()
@abstractmethod
def apply(self, balance):
"""Abstract method to apply transaction to balance"""
pass
class IncomeTransaction(Transaction):
"""Concrete class for income transactions"""
def apply(self, balance):
return balance + self.amount
class ExpenseTransaction(Transaction):
"""Concrete class for expense transactions"""
def apply(self, balance):
if balance < self.amount:
raise ValueError("Insufficient balance")
return balance - self.amount
class TransactionFactory:
"""Factory class to create transaction objects"""
@staticmethod
def create_transaction(trans_type, amount, category, date=None, note=""):
if trans_type == "income":
return IncomeTransaction(amount, category, date, note)
elif trans_type == "expense":
return ExpenseTransaction(amount, category, date, note)
else:
raise ValueError("Invalid transaction type")
class Account:
"""Singleton class for managing the account"""
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Account, cls).__new__(cls)
cls._instance.balance = 0
cls._instance.transactions = []
return cls._instance
def add_transaction(self, trans_type, amount, category, date=None, note=""):
"""Add a transaction to the account"""
with operation_logger(f"Add {trans_type} transaction"):
transaction = TransactionFactory.create_transaction(trans_type, amount, category, date, note)
self.balance = transaction.apply(self.balance)
self.transactions.append(transaction)
print(f"Transaction audit: {transaction.audit()}") # Call dynamically added audit method
return transaction
def get_balance(self):
"""Return the current balance"""
return self.balance
def get_transactions(self, category=None, start_date=None, end_date=None):
"""Filter transactions by category, start date, or end date"""
result = self.transactions
if category:
result = [t for t in result if t.category == category]
if start_date:
result = [t for t in result if t.date >= start_date]
if end_date:
result = [t for t in result if t.date <= end_date]
return result
def get_category_summary(self, category):
"""Calculate total income and expense for a category"""
total_income = sum(t.amount for t in self.transactions
if t.category == category and isinstance(t, IncomeTransaction))
total_expense = sum(t.amount for t in self.transactions
if t.category == category and isinstance(t, ExpenseTransaction))
return {"income": total_income, "expense": total_expense}
3. 文件操作(storage.py)
实现上下文管理和多线程保存。
python
# storage.py
import json
import os
from contextlib import contextmanager
from threading import Thread
from logger import operation_logger
class Storage:
def __init__(self, filename="data.json"):
self.filename = filename
@contextmanager
def file_handler(self, mode):
"""上下文管理器:管理文件操作"""
file = open(self.filename, mode)
try:
yield file
finally:
file.close()
def save_async(self, account):
"""异步保存数据"""
def save_task():
with operation_logger("Save data"):
data = {
"balance": account.balance,
"transactions": [
{
"id": t.id,
"amount": t.amount,
"category": t.category,
"trans_type": "income" if isinstance(t, IncomeTransaction) else "expense",
"date": t.date,
"note": t.note
} for t in account.transactions
]
}
with self.file_handler("w") as f:
json.dump(data, f, indent=4)
thread = Thread(target=save_task)
thread.start()
return thread
def load(self):
"""加载数据"""
if not os.path.exists(self.filename):
return None
with operation_logger("Load data"):
with self.file_handler("r") as f:
return json.load(f)
4. 用户界面(menu.py)
实现交互界面,支持异步操作。
python
# menu.py
from account import Account, TransactionFactory, IncomeTransaction, ExpenseTransaction # Added imports
from storage import Storage
from logger import operation_logger
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
def validate_date(date_str):
"""验证日期格式"""
try:
datetime.strptime(date_str, "%Y-%m-%d")
return True
except ValueError:
return False
def async_summary(account, category):
"""异步计算类别统计"""
return account.get_category_summary(category)
def main_menu():
account = Account()
storage = Storage()
# 加载数据
with operation_logger("Initialize account"):
data = storage.load()
if data:
account.balance = data["balance"]
for t in data["transactions"]:
account.add_transaction(
t["trans_type"], t["amount"], t["category"], t["date"], t["note"]
)
# 动态添加统计方法(元编程)
def monthly_summary(self, year, month):
"""动态添加:按月统计"""
total_income = sum(t.amount for t in self.transactions
if t.date.startswith(f"{year}-{month:02d}") and isinstance(t, IncomeTransaction))
total_expense = sum(t.amount for t in self.transactions
if t.date.startswith(f"{year}-{month:02d}") and isinstance(t, ExpenseTransaction))
return {"income": total_income, "expense": total_expense}
setattr(Account, "monthly_summary", monthly_summary)
with ThreadPoolExecutor(max_workers=2) as executor:
while True:
print("\\n=== Personal Finance Manager ===")
print("1. Add Income")
print("2. Add Expense")
print("3. View Balance")
print("4. View Transactions")
print("5. Filter Transactions")
print("6. Category Summary")
print("7. Monthly Summary")
print("8. Exit")
choice = input("Enter choice (1-8): ")
try:
if choice in ["1", "2"]:
with operation_logger(f"Add {'income' if choice == '1' else 'expense'}"):
amount = float(input("Enter amount: "))
category = input("Enter category (e.g., Salary, Food): ")
date = input("Enter date (YYYY-MM-DD, press Enter for today): ")
if date and not validate_date(date):
print("Invalid date format!")
continue
note = input("Enter note (optional): ")
trans_type = "income" if choice == "1" else "expense"
account.add_transaction(trans_type, amount, category, date or None, note)
print(f"{trans_type.capitalize()} added successfully!")
elif choice == "3":
print(f"Current Balance: ${account.get_balance():.2f}")
elif choice == "4":
transactions = account.get_transactions()
if not transactions:
print("No transactions found.")
for t in transactions:
print(f"ID: {t.id}, Type: {'Income' if isinstance(t, IncomeTransaction) else 'Expense'}, "
f"Amount: ${t.amount:.2f}, Category: {t.category}, Date: {t.date}, Note: {t.note}")
elif choice == "5":
category = input("Enter category to filter (press Enter to skip): ")
start_date = input("Enter start date (YYYY-MM-DD, press Enter to skip): ")
if start_date and not validate_date(start_date):
print("Invalid start date!")
continue
end_date = input("Enter end date (YYYY-MM-DD, press Enter to skip): ")
if end_date and not validate_date(end_date):
print("Invalid end date!")
continue
transactions = account.get_transactions(category or None, start_date or None, end_date or None)
if not transactions:
print("No transactions found.")
for t in transactions:
print(f"ID: {t.id}, Type: {'Income' if isinstance(t, IncomeTransaction) else 'Expense'}, "
f"Amount: ${t.amount:.2f}, Category: {t.category}, Date: {t.date}, Note: {t.note}")
elif choice == "6":
category = input("Enter category: ")
with operation_logger(f"Category summary for {category}"):
future = executor.submit(async_summary, account, category)
summary = future.result()
print(f"Category: {category}")
print(f"Total Income: ${summary['income']:.2f}")
print(f"Total Expense: ${summary['expense']:.2f}")
elif choice == "7":
year = int(input("Enter year (e.g., 2025): "))
month = int(input("Enter month (1-12): "))
with operation_logger(f"Monthly summary for {year}-{month:02d}"):
summary = account.monthly_summary(year, month)
print(f"Monthly Summary ({year}-{month:02d})")
print(f"Total Income: ${summary['income']:.2f}")
print(f"Total Expense: ${summary['expense']:.2f}")
elif choice == "8":
with operation_logger("Exit and save"):
storage.save_async(account).join() # 等待保存完成
print("Data saved. Goodbye!")
break
else:
print("Invalid choice!")
except ValueError as e:
print(f"Error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
5. 主程序(main.py)
启动程序。
python
# main.py
from menu import main_menu
if __name__ == "__main__":
main_menu()
6. 测试与调试(1小时)
- 测试用例: 添加收入(1000,Salary,2025-04-22,"Monthly salary")。 添加支出(200,Food,2025-04-22,"Dinner")。 查看余额(应为800)。 查看所有交易。 筛选Food类别的交易。 查看Salary类别的统计(异步)。 查看2025年4月月度统计。 退出并检查data.json和finance.log。
- 调试: 检查单例模式(多次实例化Account应返回同一对象)。 验证异步保存(data.json是否正确更新)。 测试元编程(audit方法和monthly_summary是否正常工作)。 确保上下文管理器记录所有操作日志。
- 优化: 使用argparse支持命令行参数。 添加删除交易功能。 使用tabulate美化输出。
三、项目测试、优化与总结(1小时)
测试
- 运行程序,逐一测试所有功能。
- 故意输入错误数据(负金额、无效日期),验证异常处理。
- 检查finance.log,确保所有操作被记录。
- 重启程序,确认数据正确加载。
优化建议
- 添加数据库支持(sqlite3)。
- 使用asyncio替代threading,提升异步性能。
- 实现更多设计模式(如观察者模式,用于交易通知)。
- 添加单元测试(使用unittest或pytest)。
总结
- 知识点回顾: 设计模式:单例模式(Account)、工厂模式(TransactionFactory)。 上下文管理:文件操作(Storage.file_handler)、日志(operation_logger)。 元编程:动态添加audit和monthly_summary方法。 多线程:异步保存(save_async)、异步统计(async_summary)。 OOP:继承、抽象类、封装。 异常处理:输入验证、文件操作错误。 模块化:逻辑分离到多个文件。
- 问题与解决方案: 记录调试中遇到的问题(如线程同步、元类调试)。 总结解决方案(如使用Thread.join()确保保存完成)。
- 扩展方向: 添加GUI(tkinter)。 集成pandas进行数据分析。 实现REST API(使用FastAPI)。
学习成果
- 高级技能:掌握了单例模式、工厂模式、上下文管理、元编程、多线程。
- 项目经验:开发了一个模块化、可扩展的命令行应用。
- 代码质量:通过设计模式和上下文管理提升了代码健壮性和可维护性。
下一步建议
- 深入学习设计模式(如观察者模式、策略模式)。
- 探索asyncio和并发编程。
- 学习元编程高级应用(如自定义描述器)。
- 开发更复杂的项目(如Web应用、数据分析工具)。
- 上一篇:Python项目创建全流程指南
- 下一篇:怎么样才算是精通 Python?
相关推荐
- 多媒体播放器(多媒体播放器怎么使用)
-
1.开启蓝牙耳机:拨动蓝牙耳机的电源开关,长按住蓝牙耳机的【电源】按钮5秒钟左右松手,直至指示灯长亮蓝色即可。注:(蓝牙耳机型号不同,具体蓝牙耳机开启方式请参考蓝牙耳机说明书操作)2.开启手机蓝牙:向...
-
- 全国教育平台登录入口(2020全国教育平台登录入口)
-
1、首先我们需要打开全国中小学教育平台官网,进入到主页当中;官网链接:https://www.xueanquan.com/(复制打开)2、在主界面右上角我们可以看到登录入口,点击“您好,请登录”,输入账号及密码;3、这里的账号老师和学生之间...
-
2026-01-28 06:51 off999
- 新手怎么从1688拿货(新手怎么从1688拿货卖烟酒怎么拿货)
-
在1688平台上进货分为以下几个步骤:1.注册并登录1688账号:首先,您需要在阿里巴巴旗下的小额批发平台1688(https://www.1688.com/)注册一个账号。如果已经有淘宝...
- 放置三国满v无限元宝(放置三国无限元宝破解版是真的吗)
-
步骤如下:。首先,你需要打开GG,然后运行游戏,并进入游戏中的商店或充值界面。接下来,输入当前游戏元宝的值并点击“搜索”按钮。然后在游戏中重新获得几个元宝,再次输入新的元宝值并点击“搜索”按钮。重复此...
- 安卓安装包下载(telegarm安卓安装包下载)
-
安装软件可以使用手机自带的应用商店,或者下载apk安装包安装手机安装包安装方法:1.apk文件是安卓手机的安装包,可以通过手机qq来安装和打开apk文件。2.首先在手机上打开qq,找到页面中的apk文...
- 财经股票怎么投资(股票初学者相关财经知识)
-
可以!答案是肯定的,不过现在对直播的主讲人还是有一些要求和限制的,首先主讲人要有从业人员资格证书,这是硬件条件。其次就是所讲的内容是什么,要符合要求,不能讲股票名称,推荐股票,通常情况下↓都是对大盘走...
- 魔兽争霸3冰封王座下载教程(魔兽争霸3冰封王座下载教程手机版)
-
没法下载,因为没有说明在什么平台下载可以在网易的官方网站上进行下载,下载地址如下:https://dz.blizzard.cn/下载的时候请选择中间的“平台完整版”,这个版本包含了《魔兽争霸3》游戏本...
- 360浏览器下载资源失败的原因
-
应该是因为手机上,没有足够的空间造成的。360浏览器如果无法下载文件,也有可能是因为网络原因,网络不通畅导致的,手机上没有足够的空间,也会造成无法下载360浏览器下载不了东西的原因可能有以下几个方面:...
- b站网页入口(b站网页入口链接怎么打开)
-
1.不存在无需付费的B站网站入口。2.这是因为B站是一个商业化的视频分享平台,为了维持平台的运营和提供更好的服务,需要收取一定的费用。3.尽管B站有一些免费的内容可以观看,但是对于一些高质量的原...
- 新商盟卷烟订货平台(新商盟卷烟网)
-
你好。其实这个新商盟系统本身就不怎么好,经常出现问题。你说的情况应该是:一,网络未连接。忘记连接和忽断网。二,系统出现问题。这还是常见。三,未点到登陆按钮,可用回车健代替。如果无法解决影响订烟,请及时...
- 极品五笔(极品五笔的拼音怎么打)
-
读音是“jípǐnwǔbǐshūrùfǎ”。“极”,读音为jí,基本含义为顶端,最高点,尽头,如登极、登峰造极;引申含义为指地球的南北两端或电路、磁体的正负两端,如极地、极圈。也常做形容词...
- pubg国际服正版下载(pubg国际服正版下载最新地址)
-
您好,以下是下载PUBGMobile国际服正式版的步骤:1.首先,您需要在您的设备上卸载任何先前安装的PUBGMobile版本。2.打开您的手机或平板电脑的应用商店(GooglePlayS...
- 视频剪辑制作教学(视频剪辑及制作)
-
可以在视频播放的时候截图,之后保存成JPEG的形式就可以了。具体的方法:设备如果是手机的话,可以在视频图标上点击打开播,到需要做成照片时暂停一下。然后再按下电源键的同时按下手机的音量减键,之后,当存了...
- 人工智能培训一般多少钱(二本人工智能就业现状)
-
python学习根据每个人的基础情况和学习的课程不同,费用自然也是不同的,一般在一万元到三万元之间,具体就看培训机构如何收费。现在市面上的培训机构很多,想学习python的朋友一定要慎重选择,根据课程...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
win7系统还原步骤图解(win7还原电脑系统的步骤)
-
python入门到脱坑 输入与输出—str()函数
-
linux软件(linux软件图标)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
