Python多模块结合案例实操?

wen python案例 2

本文目录导读:

  1. 项目结构
  2. 第一步:配置文件(config/settings.py)
  3. 第二步:数据模型(models/)
  4. 第三步:工具模块(utils/)
  5. 第四步:业务服务(services/)
  6. 第五步:主程序(main.py)
  7. 运行效果演示
  8. 模块间调用关系总结

这是一个非常实用的需求,我将为您设计一个图书管理系统的多模块案例,通过这个案例您能很好地理解 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 多模块协作。

标签: 模块化设计

抱歉,评论功能暂时关闭!