本文目录导读:
- 项目结构
- 第一步:配置文件(config/settings.py)
- 第二步:数据模型(models/)
- 第三步:工具模块(utils/)
- 第四步:业务服务(services/)
- 第五步:主程序(main.py)
- 运行效果演示
- 模块间调用关系总结
这是一个非常实用的需求,我将为您设计一个图书管理系统的多模块案例,通过这个案例您能很好地理解 Python 中模块化编程、包管理、命名空间以及模块间相互调用的思想。
项目结构
book_manager/
│
├── main.py # 程序入口
├── config/
│ └── settings.py # 配置文件(数据库路径、常量)
│
├── models/
│ ├── __init__.py
│ ├── book.py # 图书数据模型
│ └── user.py # 用户数据模型
│
├── services/
│ ├── __init__.py
│ ├── book_service.py # 图书业务逻辑
│ └── user_service.py # 用户业务逻辑
│
├── utils/
│ ├── __init__.py
│ ├── date_helper.py # 日期工具
│ └── file_helper.py # 文件读写工具
│
└── data/
└── database.json # 模拟数据库
第一步:配置文件(config/settings.py)
# 配置文件:集中管理所有常量
import os
# 项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 数据文件路径
DATA_DIR = os.path.join(BASE_DIR, "data")
DATABASE_PATH = os.path.join(DATA_DIR, "database.json")
# 图书分类
BOOK_CATEGORIES = ["科幻", "文学", "历史", "技术", "经济", "其他"]
# 借书期限(天数)
DEFAULT_BORROW_DAYS = 30
print(f"[config/settings] 数据库路径已配置: {DATABASE_PATH}")
第二步:数据模型(models/)
models/init.py
# 空文件,表示这是一个 Python 包
models/book.py
# 图书数据模型
class Book:
"""
图书类,每个图书对象代表一条数据表中的记录
"""
def __init__(self, book_id, title, author, category, is_borrowed=False):
self.book_id = book_id
self.title = title
self.author = author
self.category = category
self.is_borrowed = is_borrowed
def to_dict(self):
"""转成字典,方便 JSON 序列化"""
return {
"book_id": self.book_id,
"title": self.title,
"author": self.author,
"category": self.category,
"is_borrowed": self.is_borrowed
}
@classmethod
def from_dict(cls, data_dict):
"""从字典重建 Book 对象"""
return cls(
book_id=data_dict["book_id"],
title=data_dict["title"],
author=data_dict["author"],
category=data_dict["category"],
is_borrowed=data_dict.get("is_borrowed", False)
)
def __str__(self):
status = "已借出" if self.is_borrowed else "可借阅"
return f"[{self.book_id}] 《{self.title}》 - {self.author} ({self.category}) {status}"
models/user.py
# 用户数据模型
class User:
def __init__(self, user_id, name, phone):
self.user_id = user_id
self.name = name
self.phone = phone
self.borrowed_books = [] # 借用图书ID列表
def to_dict(self):
return {
"user_id": self.user_id,
"name": self.name,
"phone": self.phone,
"borrowed_books": self.borrowed_books
}
@classmethod
def from_dict(cls, data_dict):
user = cls(
user_id=data_dict["user_id"],
name=data_dict["name"],
phone=data_dict["phone"]
)
user.borrowed_books = data_dict.get("borrowed_books", [])
return user
def __str__(self):
return f"[{self.user_id}] {self.name} (手机: {self.phone}, 借书数: {len(self.borrowed_books)})"
第三步:工具模块(utils/)
utils/date_helper.py
# 日期工具函数
from datetime import datetime, timedelta
def get_current_date():
"""获取当前日期字符串"""
return datetime.now().strftime("%Y-%m-%d")
def calculate_due_date(days=30):
"""计算到期日"""
return (datetime.now() + timedelta(days=days)).strftime("%Y-%m-%d")
def is_overdue(due_date_str):
"""判断是否逾期(简化版:逾期 = > 到期日)"""
due_date = datetime.strptime(due_date_str, "%Y-%m-%d")
return datetime.now() > due_date
utils/file_helper.py
# 文件读写工具
import json
import os
def read_json(file_path):
"""读取 JSON 文件,返回 Python 对象"""
if not os.path.exists(file_path):
print(f"[file_helper] 文件不存在: {file_path},初始化空数据")
return {"books": [], "users": []}
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
print(f"[file_helper] 成功读取文件: {file_path}")
return data
def write_json(file_path, data):
"""将 Python 对象写入 JSON 文件"""
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
print(f"[file_helper] 成功写入文件: {file_path}")
第四步:业务服务(services/)
services/book_service.py
# 图书业务逻辑
from models.book import Book
from utils.file_helper import read_json, write_json
from config.settings import DATABASE_PATH
class BookService:
"""图书服务类,封装图书相关操作"""
def __init__(self):
self.books = [] # Book 对象列表
self.load_books()
def load_books(self):
"""从数据库加载图书数据"""
data = read_json(DATABASE_PATH)
self.books = [Book.from_dict(item) for item in data.get("books", [])]
print(f"[BookService] 已加载 {len(self.books)} 本图书")
def save_books(self):
"""保存图书数据到数据库(不破坏用户数据)"""
data = read_json(DATABASE_PATH)
data["books"] = [book.to_dict() for book in self.books]
write_json(DATABASE_PATH, data)
def add_book(self, title, author, category):
"""添加新书"""
new_id = len(self.books) + 1
book = Book(book_id=new_id, title=title, author=author, category=category)
self.books.append(book)
self.save_books()
print(f"[BookService] 添加图书成功: {book}")
return book
def list_books(self):
"""列出所有图书"""
if not self.books:
print("[BookService] 暂无图书")
for book in self.books:
print(f" {book}")
def find_by_id(self, book_id):
"""根据ID查找图书"""
for book in self.books:
if book.book_id == book_id:
return book
print(f"[BookService] 未找到图书ID: {book_id}")
return None
def borrow_book(self, book_id):
"""借书操作"""
book = self.find_by_id(book_id)
if not book:
return False
if book.is_borrowed:
print(f"[BookService] 图书《{book.title}》已被借出")
return False
book.is_borrowed = True
self.save_books()
print(f"[BookService] 借书成功: {book}")
return True
def return_book(self, book_id):
"""还书操作"""
book = self.find_by_id(book_id)
if not book:
return False
if not book.is_borrowed:
print(f"[BookService] 图书《{book.title}》未被借出")
return False
book.is_borrowed = False
self.save_books()
print(f"[BookService] 还书成功: {book}")
return True
services/user_service.py
# 用户业务逻辑
from models.user import User
from utils.file_helper import read_json, write_json
from config.settings import DATABASE_PATH
class UserService:
def __init__(self):
self.users = []
self.load_users()
def load_users(self):
data = read_json(DATABASE_PATH)
self.users = [User.from_dict(item) for item in data.get("users", [])]
print(f"[UserService] 已加载 {len(self.users)} 位用户")
def save_users(self):
data = read_json(DATABASE_PATH)
data["users"] = [user.to_dict() for user in self.users]
write_json(DATABASE_PATH, data)
def register_user(self, name, phone):
new_id = len(self.users) + 1
user = User(user_id=new_id, name=name, phone=phone)
self.users.append(user)
self.save_users()
print(f"[UserService] 注册用户成功: {user}")
return user
def list_users(self):
if not self.users:
print("[UserService] 暂无用户")
for user in self.users:
print(f" {user}")
def find_by_id(self, user_id):
for user in self.users:
if user.user_id == user_id:
return user
print(f"[UserService] 未找到用户ID: {user_id}")
return None
def borrow_book_for_user(self, user_id, book_id):
"""为用户借书(同时更新 user 和 book 的状态)"""
user = self.find_by_id(user_id)
if not user:
return False
if book_id in user.borrowed_books:
print(f"[UserService] 用户 {user.name} 已借阅此书")
return False
user.borrowed_books.append(book_id)
self.save_users()
print(f"[UserService] 用户 {user.name} 借阅图书ID {book_id} 成功")
return True
def return_book_for_user(self, user_id, book_id):
"""为用户还书"""
user = self.find_by_id(user_id)
if not user:
return False
if book_id not in user.borrowed_books:
print(f"[UserService] 用户 {user.name} 未借阅此书")
return False
user.borrowed_books.remove(book_id)
self.save_users()
print(f"[UserService] 用户 {user.name} 归还图书ID {book_id} 成功")
return True
第五步:主程序(main.py)
# 主程序入口
from config.settings import BOOK_CATEGORIES, DEFAULT_BORROW_DAYS
from services.book_service import BookService
from services.user_service import UserService
from utils.date_helper import get_current_date, calculate_due_date
def main():
print("=" * 50)
print(" 图书管理系统 - 多模块实战案例")
print(f" 当前日期: {get_current_date()}")
print("=" * 50)
# 初始化服务(两个服务模块会各自加载数据)
book_service = BookService()
user_service = UserService()
while True:
print("\n--- 主菜单 ---")
print("1. 添加图书")
print("2. 查看所有图书")
print("3. 注册用户")
print("4. 查看所有用户")
print("5. 借书(用户借书)")
print("6. 还书(用户还书)")
print("0. 退出系统")
choice = input("请选择操作: ").strip()
if choice == "1":
title = input("书名: ")
author = input("作者: ")
print(f"分类可选: {', '.join(BOOK_CATEGORIES)}")
category = input("分类: ")
book_service.add_book(title, author, category)
elif choice == "2":
print("\n--- 所有图书 ---")
book_service.list_books()
elif choice == "3":
name = input("用户名: ")
phone = input("手机号: ")
user_service.register_user(name, phone)
elif choice == "4":
print("\n--- 所有用户 ---")
user_service.list_users()
elif choice == "5":
print("\n--- 借书操作 ---")
print("当前用户列表:")
user_service.list_users()
user_id = int(input("用户ID: "))
print("\n当前可借图书:")
# 只显示未被借出的书(我们结合 book_service 的数据)
for book in book_service.books:
if not book.is_borrowed:
print(f" {book}")
book_id = int(input("图书ID: "))
# 多模块协作:先更新图书状态,再更新用户记录
if book_service.borrow_book(book_id):
user_service.borrow_book_for_user(user_id, book_id)
print(f"借书成功!到期日: {calculate_due_date(DEFAULT_BORROW_DAYS)}")
elif choice == "6":
print("\n--- 还书操作 ---")
print("当前用户列表:")
user_service.list_users()
user_id = int(input("用户ID: "))
user = user_service.find_by_id(user_id)
if user and user.borrowed_books:
print(f"用户 {user.name} 已借阅的图书ID: {user.borrowed_books}")
book_id = int(input("待归还图书ID: "))
if book_service.return_book(book_id):
user_service.return_book_for_user(user_id, book_id)
elif choice == "0":
print("感谢使用图书管理系统,再见!")
break
else:
print("无效输入,请重新选择")
if __name__ == "__main__":
main()
运行效果演示
==================================================
图书管理系统 - 多模块实战案例
当前日期: 2025-04-13
==================================================
[config/settings] 数据库路径已配置: /.../book_manager/data/database.json
[file_helper] 文件不存在: /.../book_manager/data/database.json,初始化空数据
[BookService] 已加载 0 本图书
[UserService] 已加载 0 位用户
--- 主菜单 ---
1. 添加图书
2. 查看所有图书
3. 注册用户
4. 查看所有用户
5. 借书(用户借书)
6. 还书(用户还书)
0. 退出系统
请选择操作: 1
书名: 三体
作者: 刘慈欣
分类可选: 科幻, 文学, 历史, 技术, 经济, 其他
分类: 科幻
[BookService] 添加图书成功: [1] 《三体》 - 刘慈欣 (科幻) 可借阅
... (继续操作)
模块间调用关系总结
main.py
├── 导入 config/settings.py → 读取常量信息
├── 导入 services/book_service.py → 实例化 BookService
│ ├── 导入 models/book.py → 使用 Book 数据模型
│ ├── 导入 utils/file_helper.py → 读写 JSON 文件
│ └── 导入 config/settings.py → 读取数据库路径
│
├── 导入 services/user_service.py → 实例化 UserService
│ ├── 导入 models/user.py → 使用 User 数据模型
│ ├── 导入 utils/file_helper.py → 读写 JSON 文件
│ └── 导入 config/settings.py → 读取数据库路径
│
└── 导入 utils/date_helper.py → 日期工具函数(直接在 main 中调用)
关键设计思想:
- 高内聚:每个模块只负责本领域的事情(模型管数据定义、服务管业务逻辑、工具管公共功能)
- 低耦合:模块之间通过明确的接口(函数/方法)交互,不直接访问内部数据
- 单一职责:
BookService不关心用户逻辑,UserService不直接操作图书数据(通过book_service操作) - 可复用:
file_helper.py可以被任何需要文件读写的地方调用
这个案例涵盖了真实项目中典型的模块拆分方式,适合理解和练习 Python 多模块协作。
标签: 模块化设计