Skip to content

需求 ​

某社区图书馆要开发一个简单的图书管理系统,支持会员登录、图书借阅、图书归还。

功能说明
会员登录卡号 + 密码登录,登录成功后才能使用系统
借书借阅库存有余量的图书
还书归还已借阅的图书
查看我的借阅展示当前会员已借阅的图书列表
退出系统—

借阅规则:普通会员最多借 3 本;VIP 会员最多借 6 + VIP等级 本(等级默认 1)。库存不足或已达上限时不能借。

类的设计 ​

类职责
Book一本书:编号、标题、作者、总数、可用数量(私有);借出/归还/查可用
Member(抽象类)会员公共部分:卡号、姓名、密码(私有)、借阅列表(私有);借书/还书/取密码/取借阅列表;抽象方法 get_max_books()
NormalMember普通会员:get_max_books() 返回 3
VIPMemberVIP 会员:多一个 vip_level,get_max_books() 返回 6 + vip_level
LibrarySystem图书馆系统:加载数据、登录、借书、还书、查看借阅、主循环

抽象类:规定子类必须实现什么 ​

python
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 自己说不清"最多能借几本",但要求每个子类必须说清楚。

关键代码 ​

图书:私有可用数量 + 借还 ​

python
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

会员:借书时先查上限,再查库存 ​

python
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

两种会员:用多态实现"上限不同" ​

python
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 加载数据(按卡号前缀判断会员类型) ​

python
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 里是 [{"卡号","姓名","密码","会员等级"}, ...](普通会员没有"会员等级"字段)。

主循环 ​

python
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("无效的选项,请重新选择!")

相关 ​

练习题 ​

一、知识回顾(读完直接做下面的实践题) ​

  1. 抽象类需要继承 abc 模块的 ABC 类,抽象方法用 @abstractmethod 装饰
  2. 抽象类的作用:规定子类必须实现哪些方法;它本身不能被直接实例化
  3. 普通会员最多借 3 本;VIP 会员最多借 6 + VIP等级 本
  4. 借书时要先判断会员的已借数量是否达到上限,再判断图书的**库存(可用数量)**是否足够
  5. 加载会员数据时,用卡号的**前缀(startswith)**判断是普通会员还是 VIP(N 开头/V 开头)
  6. 图书的可用数量设为私有属性,对外通过 get_available_num() 读取
  7. 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())