需求
某社区图书馆要开发一个简单的图书管理系统,支持会员登录、图书借阅、图书归还。
| 功能 | 说明 |
|---|---|
| 会员登录 | 卡号 + 密码登录,登录成功后才能使用系统 |
| 借书 | 借阅库存有余量的图书 |
| 还书 | 归还已借阅的图书 |
| 查看我的借阅 | 展示当前会员已借阅的图书列表 |
| 退出系统 | — |
借阅规则:普通会员最多借 3 本;VIP 会员最多借 6 + VIP等级 本(等级默认 1)。库存不足或已达上限时不能借。
类的设计
| 类 | 职责 |
|---|---|
Book | 一本书:编号、标题、作者、总数、可用数量(私有);借出/归还/查可用 |
Member(抽象类) | 会员公共部分:卡号、姓名、密码(私有)、借阅列表(私有);借书/还书/取密码/取借阅列表;抽象方法 get_max_books() |
NormalMember | 普通会员:get_max_books() 返回 3 |
VIPMember | VIP 会员:多一个 vip_level,get_max_books() 返回 6 + vip_level |
LibrarySystem | 图书馆系统:加载数据、登录、借书、还书、查看借阅、主循环 |
抽象类:规定子类必须实现什么
from abc import ABC, abstractmethod
class Member(ABC): # 继承 ABC(Abstract Base Class)
...
@abstractmethod
def get_max_books(self) -> int:
pass # 只有声明,没有实现IMPORTANT
抽象类是一种只能被继承、不能被直接实例化的类,作用是规定子类必须实现哪些方法,强制子类遵守统一的代码规范。Python 里的抽象类需要继承 abc 模块的 ABC,抽象方法用 @abstractmethod 装饰。 在这里它的价值是:Member 自己说不清"最多能借几本",但要求每个子类必须说清楚。
关键代码
图书:私有可用数量 + 借还
class Book:
def __init__(self, book_id, title, author, total_num):
self.book_id = book_id
self.title = title
self.author = author
self.total_num = total_num
self.__available_num = total_num # 可用数量(私有)
def borrow_book(self):
if self.__available_num > 0:
self.__available_num -= 1
return True
return False
def return_book(self):
self.__available_num += 1
def get_available_num(self):
return self.__available_num会员:借书时先查上限,再查库存
class Member(ABC):
def borrow_book(self, book: Book):
# 1. 达到最大借阅数量就不给借
if len(self.__borrowed_books) >= self.get_max_books():
print("借阅失败,您的借阅数量已达到最大限制!")
return False
# 2. 书籍还有余量才借
if book.borrow_book():
self.__borrowed_books.append(book)
print(f"{self.name} 已成功借阅图书 {book.title}")
return True
else:
print(f"借阅失败,图书 {book.title} 已被借完!")
return False两种会员:用多态实现"上限不同"
class NormalMember(Member):
def get_max_books(self) -> int:
return 3
class VIPMember(Member):
def __init__(self, member_id, name, password, vip_level):
super().__init__(member_id, name, password)
self.vip_level = vip_level
def get_max_books(self) -> int:
return 6 + self.vip_level系统:从 JSON 加载数据(按卡号前缀判断会员类型)
class LibrarySystem:
def __init__(self):
self.books = {} # {"AI001": Book对象, ...}
self.members = {} # {"V001": Member对象, ...}
self.current_member: Member | None = None
self.load_books_data()
self.load_members_data()
def load_members_data(self):
with open("data/members.json", "r", encoding="utf-8") as f:
members_data = json.load(f)
for member in members_data:
if member['卡号'].startswith('N'):
self.members[member['卡号']] = NormalMember(member['卡号'], member['姓名'], member['密码'])
elif member['卡号'].startswith('V'):
self.members[member['卡号']] = VIPMember(member['卡号'], member['姓名'], member['密码'], member['会员等级'])数据结构对应关系:books.json 里是 [{"编号","标题","作者","数量"}, ...],members.json 里是 [{"卡号","姓名","密码","会员等级"}, ...](普通会员没有"会员等级"字段)。
主循环
def run(self):
if self.login(): # 登录成功才进菜单
while True:
print("1. 借阅图书 2. 归还图书 3. 查看借阅 4. 退出系统")
choice = input("请选择操作(1-4): ")
match choice:
case "1": self.borrow_book()
case "2": self.return_book()
case "3": self.show_borrowed_books()
case "4":
print("退出系统, Bye Bye ~")
break
case _: print("无效的选项,请重新选择!")相关
练习题
一、知识回顾(读完直接做下面的实践题)
- 抽象类需要继承
abc模块的ABC类,抽象方法用@abstractmethod装饰 - 抽象类的作用:规定子类必须实现哪些方法;它本身不能被直接实例化
- 普通会员最多借 3 本;VIP 会员最多借 6 + VIP等级 本
- 借书时要先判断会员的已借数量是否达到上限,再判断图书的**库存(可用数量)**是否足够
- 加载会员数据时,用卡号的**前缀(startswith)**判断是普通会员还是 VIP(N 开头/V 开头)
- 图书的可用数量设为私有属性,对外通过
get_available_num()读取 LibrarySystem里self.books和self.members都是字典(类型),键是编号/卡号,值是对象
二、裸写题
- 提示(先自己想,实在想不出再点开)
一级 · 思路:父类定规矩(抽象方法),子类各自给答案 二级 · 方法:
class Member(ABC)+@abstractmethod def get_max_books(self)三级 · 骨架:VIP 的__init__里先super().__init__(...)再存vip_level- 参考答案(做完再点开)
python# 2-1(Book 类见 2-2) from abc import ABC, abstractmethod class Member(ABC): def __init__(self, member_id, name, password): self.member_id = member_id self.name = name self.__password = password self.__borrowed_books = [] def get_borrowed_books(self): return self.__borrowed_books @abstractmethod def get_max_books(self) -> int: pass class NormalMember(Member): def get_max_books(self) -> int: return 3 class VIPMember(Member): def __init__(self, member_id, name, password, vip_level): super().__init__(member_id, name, password) self.vip_level = vip_level def get_max_books(self) -> int: return 6 + self.vip_level # 测试 normal = NormalMember("N001", "小王", "123456") vip = VIPMember("V001", "王林", "666888", 6) print(normal.get_max_books(), vip.get_max_books()) # 3 12- 提示(先自己想,实在想不出再点开)
一级 · 思路:可用数量只让类内部改,外面只能读 二级 · 方法:
self.__available_num/get_available_num()三级 · 骨架:if self.__available_num > 0: self.__available_num -= 1; return True- 参考答案(做完再点开)
python# 2-2 class Book: def __init__(self, book_id, title, author, total_num): self.book_id = book_id self.title = title self.author = author self.total_num = total_num self.__available_num = total_num def borrow_book(self): if self.__available_num > 0: self.__available_num -= 1 return True return False def return_book(self): self.__available_num += 1 def get_available_num(self): return self.__available_num- 提示(先自己想,实在想不出再点开)
一级 · 思路:两道关卡,先自己的额度、再书的库存 二级 · 方法:
len(self.get_borrowed_books()) >= self.get_max_books()三级 · 骨架:借成功要append到借阅列表里- 参考答案(做完再点开)
python# 2-3:Member 类里的借书方法(Book、NormalMember 等定义见 2-1 / 2-2) class Member(ABC): def __init__(self, member_id, name, password): self.member_id = member_id self.name = name self.__password = password self.__borrowed_books = [] def borrow_book(self, book: Book): if len(self.__borrowed_books) >= self.get_max_books(): print(f"{self.name} 借阅失败,已达到最大借阅数量 {self.get_max_books()} 本") return False if book.borrow_book(): self.__borrowed_books.append(book) print(f"{self.name} 已成功借阅图书 {book.title}") return True print(f"{self.name} 借阅失败,图书 {book.title} 已被借完") return False def get_borrowed_books(self): return self.__borrowed_books @abstractmethod def get_max_books(self) -> int: pass # 测试 normal = NormalMember("N001", "小王", "123456") book = Book("AI001", "LangChain入门与实践", "李智慧", 2) for i in range(4): normal.borrow_book(book) # 第 4 次会因达到上限而失败 print("剩余库存:", book.get_available_num()) book.return_book() print("归还后剩余:", book.get_available_num())
