首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >面向对象的学生图书馆2

面向对象的学生图书馆2
EN

Code Review用户
提问于 2018-11-09 11:56:25
回答 1查看 843关注 0票数 3

面向对象的学生图书馆随访

问:您如何重构这段代码,使其成为pythonic的,遵循OOP,阅读更好,并且是可管理的?如何更好地编写名称、函数和类?您如何知道需要使用哪种数据结构才能有效地管理数据?

代码语言:javascript
复制
from collections import defaultdict
from datetime import datetime, timedelta

class StudentDataBaseException(Exception): pass
class NoStudent(StudentDataBaseException): pass
class NoBook(StudentDataBaseException): pass

"""To keep of a record of students 
 who have yet to return books and their due dates"""
class CheckedOut:
     loan_period = 10
     fine_per_day = 2

     def __init__(self):
         self.due_dates = {}

     def check_in(self, name):
          due_date = datetime.now() + timedelta(days=self.loan_period)
          self.due_dates[name] = due_date

     def check_out(self, name):
         current_date = datetime.now()
         if current_date > self.due_dates[name]:
             delta = current_date - self.due_dates[name]
             overdue_fine = self.fine_per_day * delta.days
             print("Fine Amount: ", overdue_fine)

# This only contains the title name for now
class BookStatus:
    def __init__(self, title):
        self.title = title

    def __repr__(self):
        return self.title

    def __hash__(self):
        return 0

    def __eq__(self, other):
        return self.title == other

# contains a set of books
class Library:
    record = CheckedOut()

    def __init__(self):
        self.books = set()

    def add_book(self, new_book):
        self.books.add(new_book)

    def display_books(self):
        if self.books:
            print("The books we have made available in our library are:\n")
            for book in self.books:
                print(book)
        else:
            print("Sorry, we have no books available in the library at the moment")

    def lend_book(self, requested_book):
        if requested_book in self.books:
            print(f'''You have now borrowed \"{requested_book}\"''')
            self.books.remove(requested_book)
            return True

        else:
            print(f'''Sorry, \"{requested_book}\" is not there in our library at the moment''')
            return False

# container for students
class StudentDatabase:
    def __init__(self):
        self.books = defaultdict(set)

    def borrow_book(self, name, book, library):
        if library.lend_book(book):
            self.books[name].add(book)
            return True
        return False

    def return_book(self, name, book, library):
        if book not in self.books[name]:
            raise NoBook(f'''\"{name}\" doesn't seem to have borrowed "{book}"''')
            return False
        else:
            library.add_book(book)
            self.books[name].remove(book)
            return True

    def students_with_books(self):
        for name, books in self.books.items():
            if books:
                yield name, books


def borrow_book(library, book_tracking):
    name = input("Student Name: ")
    book = BookStatus(input("Book Title: "))

    if book_tracking.borrow_book(name, book, library):
        library.record.check_in(name)

def return_book(library, book_tracking):
    name = input("Student Name: ")
    returned_book = BookStatus(input("Book Title: "))

    if book_tracking.return_book(name, returned_book, library):
        library.record.check_out(name)


line = "_" * 100
menu = "Library Management System \n\n \
1) Add Book \n \
2) Display all Books \n \
3) Borrow a Book \n \
4) Return a Book \n \
5) Lending Record \n \
6) Exit"

library = Library()
book_tracking = StudentDatabase()

while True:
    print(line)
    print(menu)

    choice = get_valid_choice(min=1, max=6)
    print(line)

    if choice == 1:
        library.add_book(BookStatus(input("Book Title: ")))

    elif choice == 2:
        library.display_books()

    elif choice == 3:
        borrow_book(library, book_tracking)

    elif choice == 4:
        return_book(library, book_tracking)

    elif choice == 5:
        students = tuple(book_tracking.students_with_books())
        if students:
            for name, book in students:
                print(f"{name}: {book}")
        else:
            print("No students have borrowed books at the moment")

    elif choice == 6:
        break
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-11-09 13:32:14

我认为你的代码的一般结构很差。

  1. 即使借出一本书,图书馆也拥有一本书。
  2. 您的代码似乎无法处理同名的书籍。
  3. 在不运行代码的情况下,您似乎可以通过取出一本书,然后返回另一本书来偷书。
  4. BookStatus实现得非常糟糕。我会使用dataclasses
  5. 你实际上是在创建内存数据库。所以你应该设计数据库,然后围绕着它的代码。

从数据库设计开始,您有三件事:

  • 贷款
  • 人物

是的,没有图书馆,因为所有的书都在你的图书馆里。每本书都可以借很多次,但只能借一本书。每个人可以有多个贷款,但每个贷款只能给一个人。

因此,您的Loan对象应该具有图书和person的ID,但是图书和person都不应该有任何指向其他数据库项的链接。

然后,我们可以在Python中创建这些对象和表。我正在使用dataclasses (Python 3.6)和typing快速构建对象。由于我们已经将typing用于dataclass,所以我决定将程序的其余部分完全输入,允许静态分析器(如形象化 )检查代码中的错误。这允许使用以下基本代码:

代码语言:javascript
复制
from dataclasses import dataclass
from typing import Optional, Set, Mapping, TypeVar, Dict, Type, Iterator
from datetime import datetime, timedelta

T = TypeVar('T')


@dataclass
class Book:
    id: int
    name: str


@dataclass
class Person:
    id: int
    name: str


@dataclass
class Loan:
    id: int
    book: Book
    person: Person
    checkout: datetime
    due: datetime
    checkin: Optional[datetime]


class Table(Mapping[int, T]):
    _db: Dict[int, T]

    def __init__(self, type: Type[T]) -> None:
        self._db = {}
        self._type = type

    def __getitem__(self, key: int) -> T:
        return self._db[key]

    def __iter__(self) -> Iterator[T]:
        return iter(self._db)

    def __len__(self) -> int:
        return len(self._db)


books = Table(Book)
people = Table(Person)
loans = Table(Loan)

这样我们就可以轻松地添加其他功能:

代码语言:javascript
复制
class Table(Mapping[int, T]):
    # Other code
    def add(self, *args, **kwargs) -> None:
        key = len(self)
        self._db[key] = self._type(key, *args, **kwargs)

    def display(self) -> None:
        for value in self.values():
            print(value)


def borrow_book(person: int, book: int, loan_days: int) -> None:
    checkout = datetime.now()
    loans.add(
        books[book],
        people[person],
        checkout,
        checkout + timedelta(days=loan_days),
        None
    )


def return_book(loan: int) -> None:
    loans[loan].checkin = datetime.now()


def display_active_loans() -> None:
    has_active = False
    for loan in loans.values():
        if loan.checkin is not None:
            continue
        has_active = True
        print(f'{loan.id}: {loan.person.name} -> {loan.book.name}')
    if not has_active:
        print('No active loans')

使用起来相当容易,只需使用is:

代码语言:javascript
复制
books.add('Title')
books.display()
people.add('Student')
people.display()
borrow_book(0, 0, 10)
display_active_loans()
return_book(0)
display_active_loans()
代码语言:javascript
复制
from dataclasses import dataclass
from typing import Optional, Set, Mapping, TypeVar, Dict, Type, Iterator
from datetime import datetime, timedelta

T = TypeVar('T')


@dataclass
class Book:
    id: int
    name: str


@dataclass
class Person:
    id: int
    name: str


@dataclass
class Loan:
    id: int
    book: Book
    person: Person
    checkout: datetime
    due: datetime
    checkin: Optional[datetime]


class Table(Mapping[int, T]):
    _db: Dict[int, T]

    def __init__(self, type: Type[T]) -> None:
        self._db = {}
        self._type = type

    def __getitem__(self, key: int) -> T:
        return self._db[key]

    def __iter__(self) -> Iterator[T]:
        return iter(self._db)

    def __len__(self) -> int:
        return len(self._db)

    def add(self, *args, **kwargs) -> None:
        key = len(self)
        self._db[key] = self._type(key, *args, **kwargs)

    def display(self) -> None:
        for value in self.values():
            print(value)


books = Table(Book)
people = Table(Person)
loans = Table(Loan)


def borrow_book(person: int, book: int, loan_days: int) -> None:
    checkout = datetime.now()
    loans.add(
        books[book],
        people[person],
        checkout,
        checkout + timedelta(days=loan_days),
        None
    )


def return_book(loan: int) -> None:
    loans[loan].checkin = datetime.now()


def display_active_loans() -> None:
    has_active = False
    for loan in loans.values():
        if loan.checkin is not None:
            continue
        has_active = True
        print(f'{loan.id}: {loan.person.name} -> {loan.book.name}')
    if not has_active:
        print('No active loans')
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/207303

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档