图书管理系统是一个典型的面向对象编程(OOP)实践项目,常用于教学和课程设计。它模拟图书馆对图书、读者、借阅记录等核心资源的增删改查(CRUD)管理。下面我将为你提供一个**轻量级、可运行、结构清晰、具备基本功能的 Python 图书管理系统实现**,采用面向对象设计,不依赖数据库(使用内存字典 + JSON 持久化),并附带详细解释。
---
### ✅ 功能概览
- 图书管理:添加/删除/查询/修改图书(ISBN、书名、作者、分类、库存)
- 读者管理:注册/注销/查询读者(读者ID、姓名、联系方式)
- 借阅管理:借书、还书、查看某读者的借阅历史、查看某图书的借阅状态
- 数据持久化:启动时从 `library_data.json` 加载,退出时自动保存
- 命令行交互界面(简易 CLI)
---
### 🧩 完整可运行代码(Python 3.7+)
```python
import json
import os
from datetime import datetime
from typing import Dict, List, Optional, Any
DATA_FILE = "library_data.json"
class Book:
def __init__(self, isbn: str, title: str, author: str, category: str, stock: int = 1):
self.isbn = isbn.strip()
self.title = title.strip()
self.author = author.strip()
self.category = category.strip()
self.stock = max(0, stock)
self._borrowed_count = 0 # 统计总借出次数(非实时状态)
def to_dict(self) -> Dict[str, Any]:
return {
"isbn": self.isbn,
"title": self.title,
"author": self.author,
"category": self.category,
"stock": self.stock,
"borrowed_count": self._borrowed_count,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Book':
b = cls(
isbn=data["isbn"],
title=data["title"],
author=data["author"],
category=data["category"],
stock=data["stock"]
)
b._borrowed_count = data.get("borrowed_count", 0)
return b
def __str__(self) -> str:
return f"[{self.isbn}]《{self.title}》— {self.author} ({self.category}) | 库存: {self.stock}"
class Reader:
def __init__(self, reader_id: str, name: str, contact: str):
self.reader_id = reader_id.strip()
self.name = name.strip()
self.contact = contact.strip()
def to_dict(self) -> Dict[str, Any]:
return {
"reader_id": self.reader_id,
"name": self.name,
"contact": self.contact,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Reader':
return cls(data["reader_id"], data["name"], data["contact"])
def __str__(self) -> str:
return f"读者[{self.reader_id}] {self.name} | {self.contact}"
class BorrowRecord:
def __init__(self, record_id: int, reader_id: str, isbn: str, borrow_time: str, return_time: Optional[str] = None):
self.record_id = record_id
self.reader_id = reader_id
self.isbn = isbn
self.borrow_time = borrow_time
self.return_time = return_time # None 表示未归还
def to_dict(self) -> Dict[str, Any]:
return {
"record_id": self.record_id,
"reader_id": self.reader_id,
"isbn": self.isbn,
"borrow_time": self.borrow_time,
"return_time": self.return_time,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'BorrowRecord':
return cls(
record_id=data["record_id"],
reader_id=data["reader_id"],
isbn=data["isbn"],
borrow_time=data["borrow_time"],
return_time=data.get("return_time")
)
def is_returned(self) -> bool:
return self.return_time is not None
def __str__(self) -> str:
status = "已归还" if self.is_returned() else "借阅中"
return f"#{self.record_id} [{self.reader_id}] ← {self.isbn} | {self.borrow_time} → {self.return_time or '—'} ({status})"
class LibrarySystem:
def __init__(self):
self.books: Dict[str, Book] = {} # isbn → Book
self.readers: Dict[str, Reader] = {} # reader_id → Reader
self.records: List[BorrowRecord] = [] # 所有借阅记录(含已还)
self._next_record_id = 1
self._load_data()
def _load_data(self):
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
self.books = {isbn: Book.from_dict(d) for isbn, d in data.get("books", {}).items()}
self.readers = {rid: Reader.from_dict(d) for rid, d in data.get("readers", {}).items()}
self.records = [BorrowRecord.from_dict(r) for r in data.get("records", [])]
self._next_record_id = data.get("next_record_id", 1)
print(f"✅ 已加载 {len(self.books)} 本图书、{len(self.readers)} 位读者、{len(self.records)} 条借阅记录。")
except Exception as e:
print(f"⚠️ 数据加载失败,使用空系统:{e}")
else:
print("ℹ️ 首次运行,初始化空图书管理系统。")
def _save_data(self):
data = {
"books": {isbn: b.to_dict() for isbn, b in self.books.items()},
"readers": {rid: r.to_dict() for rid, r in self.readers.items()},
"records": [r.to_dict() for r in self.records],
"next_record_id": self._next_record_id,
}
try:
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"❌ 保存数据失败:{e}")
# ————— 图书管理 —————
def add_book(self, isbn: str, title: str, author: str, category: str, stock: int = 1) -> bool:
if not isbn or isbn in self.books:
return False
self.books[isbn] = Book(isbn, title, author, category, stock)
return True
def remove_book(self, isbn: str) -> bool:
if isbn not in self.books:
return False
# 检查是否还有未归还的借阅
if any(r.isbn == isbn and not r.is_returned() for r in self.records):
return False # 有未还书,禁止删除
del self.books[isbn]
return True
def find_book_by_isbn(self, isbn: str) -> Optional[Book]:
return self.books.get(isbn)
def search_books(self, keyword: str) -> List[Book]:
kw = keyword.lower()
return [
b for b in self.books.values()
if kw in b.isbn.lower() or kw in b.title.lower() or kw in b.author.lower() or kw in b.category.lower()
]
# ————— 读者管理 —————
def add_reader(self, reader_id: str, name: str, contact: str) -> bool:
if not reader_id or reader_id in self.readers:
return False
self.readers[reader_id] = Reader(reader_id, name, contact)
return True
def remove_reader(self, reader_id: str) -> bool:
if reader_id not in self.readers:
return False
# 检查该读者是否有未归还图书
if any(r.reader_id == reader_id and not r.is_returned() for r in self.records):
return False
del self.readers[reader_id]
return True
def find_reader(self, reader_id: str) -> Optional[Reader]:
return self.readers.get(reader_id)
# ————— 借阅管理 —————
def borrow_book(self, reader_id: str, isbn: str) -> str: # 返回提示信息
reader = self.find_reader(reader_id)
if not reader:
return "❌ 读者不存在。"
book = self.find_book_by_isbn(isbn)
if not book:
return "❌ 图书不存在。"
if book.stock <= 0:
return "❌ 图书库存不足,无法借阅。"
# 创建借阅记录
now = datetime.now().strftime("%Y-%m-%d %H:%M")
record = BorrowRecord(
record_id=self._next_record_id,
reader_id=reader_id,
isbn=isbn,
borrow_time=now
)
self.records.append(record)
self._next_record_id += 1
# 更新库存与统计
book.stock -= 1
book._borrowed_count += 1
return f"✅ 借阅成功!记录 #{record.record_id} | {book.title}"
def return_book(self, reader_id: str, isbn: str) -> str:
# 查找该读者最近一次未归还的该书借阅记录(FIFO 简化)
for r in reversed(self.records):
if r.reader_id == reader_id and r.isbn == isbn and not r.is_returned():
r.return_time = datetime.now().strftime("%Y-%m-%d %H:%M")
# 恢复库存
if isbn in self.books:
self.books[isbn].stock += 1
return f"✅ 归还成功!记录 #{r.record_id}"
return "❌ 未找到有效的借阅记录(可能已归还或不存在)。"
def get_reader_history(self, reader_id: str) -> List[BorrowRecord]:
return [r for r in self.records if r.reader_id == reader_id]
def get_book_status(self, isbn: str) -> List[BorrowRecord]:
return [r for r in self.records if r.isbn == isbn]
# ————— 工具方法 —————
def list_all_books(self) -> List[Book]:
return list(self.books.values())
def list_all_readers(self) -> List[Reader]:
return list(self.readers.values())
def get_statistics(self) -> Dict[str, Any]:
total_books = len(self.books)
total_readers = len(self.readers)
total_borrows = len(self.records)
active_borrows = sum(1 for r in self.records if not r.is_returned())
return {
"total_books": total_books,
"total_readers": total_readers,
"total_borrows": total_borrows,
"active_borrows": active_borrows,
}
# ————— CLI 交互界面 —————
def main():
lib = LibrarySystem()
print("📚 欢迎使用【简易图书管理系统】")
print("=" * 40)
while True:
print("\n📖 主菜单:")
print("1. 图书管理(添加/删除/查询)")
print("2. 读者管理(注册/注销/查询)")
print("3. 借阅管理(借书/还书/历史)")
print("4. 系统统计与全部列表")
print("0. 退出系统(自动保存)")
choice = input("\n请选择操作 [0-4]:").strip()
print()
if choice == "0":
lib._save_data()
print("👋 已保存数据,再见!")
break
elif choice == "1":
print("—— 图书管理 ——")
print("a. 添加图书 b. 删除图书 c. 按ISBN查询 d. 关键词搜索 e. 查看全部")
sub = input("请选择 [a-e]:").strip().lower()
if sub == "a":
isbn = input("ISBN:").strip()
title = input("书名:").strip()
author = input("作者:").strip()
cat = input("分类:").strip()
stock = int(input("库存(默认1):") or "1")
if lib.add_book(isbn, title, author, cat, stock):
print("✅ 图书添加成功。")
else:
print("❌ ISBN重复或输入无效。")
elif sub == "b":
isbn = input("请输入要删除的ISBN:").strip()
if lib.remove_book(isbn):
print("✅ 图书删除成功。")
else:
print("❌ 删除失败(图书不存在 或 有未归还记录)。")
elif sub == "c":
isbn = input("ISBN:").strip()
b = lib.find_book_by_isbn(isbn)
print(b if b else "❌ 未找到该图书。")
elif sub == "d":
kw = input("关键词(书名/作者/ISBN/分类):").strip()
results = lib.search_books(kw)
if results:
print(f"🔍 找到 {len(results)} 本相关图书:")
for b in results:
print(f" {b}")
else:
print("❌ 未匹配到图书。")
elif sub == "e":
books = lib.list_all_books()
if books:
print(f"📋 共 {len(books)} 本图书:")
for b in books:
print(f" {b}")
else:
print("📭 图书馆暂无藏书。")
elif choice == "2":
print("—— 读者管理 ——")
print("a. 注册读者 b. 注销读者 c. 按ID查询 d. 查看全部")
sub = input("请选择 [a-d]:").strip().lower()
if sub == "a":
rid = input("读者ID:").strip()
name = input("姓名:").strip()
cont = input("联系方式:").strip()
if lib.add_reader(rid, name, cont):
print("✅ 读者注册成功。")
else:
print("❌ ID重复或输入无效。")
elif sub == "b":
rid = input("请输入要注销的读者ID:").strip()
if lib.remove_reader(rid):
print("✅ 读者注销成功。")
else:
print("❌ 注销失败(读者不存在 或 有未归还图书)。")
elif sub == "c":
rid = input("读者ID:").strip()
r = lib.find_reader(rid)
print(r if r else "❌ 未找到该读者。")
elif sub == "d":
readers = lib.list_all_readers()
if readers:
print(f"👥 共 {len(readers)} 位读者:")
for r in readers:
print(f" {r}")
else:
print("📭 暂无注册读者。")
elif choice == "3":
print("—— 借阅管理 ——")
print("a. 借书 b. 还书 c. 查看读者借阅历史 d. 查看图书借阅状态")
sub = input("请选择 [a-d]:").strip().lower()
if sub == "a":
rid = input("读者ID:").strip()
isbn = input("ISBN:").strip()
print(lib.borrow_book(rid, isbn))
elif sub == "b":
rid = input("读者ID:").strip()
isbn = input("ISBN:").strip()
print(lib.return_book(rid, isbn))
elif sub == "c":
rid = input("读者ID:").strip()
hist = lib.get_reader_history(rid)
if hist:
print(f"📖 {rid} 的借阅历史(共{len(hist)}条):")
for r in hist:
print(f" {r}")
else:
print("📭 该读者暂无借阅记录。")
elif sub == "d":
isbn = input("ISBN:").strip()
status = lib.get_book_status(isbn)
if status:
print(f"📊 图书《{lib.find_book_by_isbn(isbn).title if lib.find_book_by_isbn(isbn) else '未知'}》借阅状态(共{len(status)}条):")
for r in status:
print(f" {r}")
else:
print("📭 该图书暂无借阅记录。")
elif choice == "4":
stats = lib.get_statistics()
print("📈 系统统计:")
print(f" 📚 总图书数:{stats['total_books']}")
print(f" 👥 总读者数:{stats['total_readers']}")
print(f" 📖 总借阅数:{stats['total_borrows']}")
print(f" ⏳ 当前借出:{stats['active_borrows']} 本")
print("\n📋 全部图书(简略):")
for b in lib.list_all_books()[:10]:
print(f" {b}")
if len(lib.list_all_books()) > 10:
print(f" ... 还有 {len(lib.list_all_books()) - 10} 本")
else:
print("❗ 无效选项,请重试。")
if __name__ == "__main__":
main()
```
---
### 🔍 解释说明
#### ✅ 设计亮点:
- **模块化类结构**:`Book`、`Reader`、`BorrowRecord` 各自封装属性与序列化逻辑,符合单一职责原则。
- **内存 + 持久化双模**:使用 `json` 文件实现轻量级持久化,避免引入 SQLite/MySQL 等外部依赖,适合教学与原型开发。
- **业务约束严谨**:
- 删除图书/读者前检查“是否存在未归还记录”,防止数据不一致;
- 借书时校验库存,还书时自动恢复库存;
- `BorrowRecord` 明确区分 `borrow_time` 和 `return_time`,支持状态判断。
- **CLI 友好交互**:分层菜单、输入提示、中文反馈,降低使用门槛。
- **健壮性处理**:异常捕获、空值防御、输入清洗(`.strip()`)、库存下限保护(`max(0, stock)`)。
#### ⚠️ 注意事项(常见问题原因):
- 若运行时报 `JSONDecodeError`,说明 `library_data.json` 文件损坏 → 可手动删除该文件重启。
- 不支持并发访问(单线程 CLI),如需 Web 版本需升级为 Flask/Django + 数据库事务。
- 当前未做输入格式校验(如 ISBN 标准化),进阶可集成 `isbnlib` 库。
#### 📈 可扩展方向(进阶建议):
- 添加图书封面图片路径字段 & 简单文件存储;
- 支持按分类/作者/借阅次数排序展示;
- 导出报表为 CSV / Excel(用 `pandas`);
- 增加管理员登录认证(用户名密码哈希);
- 封装为 Web API(FastAPI)供前端调用。
---