首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >个人图书图书馆

个人图书图书馆
EN

Code Review用户
提问于 2020-10-09 09:28:33
回答 3查看 3.9K关注 0票数 11

对于Python来说,OOP是很新的东西--而且我觉得我还没有完全得到OOP。然而,我想我应该建立一个个人的在线图书库来实践继承,方法,文档字符串等。

程序允许将书籍(漫画或小说-漫画继承自小说)添加到自己的图书馆,并创建一个自定义阅读列表。

有几种不同的方法。例如,将返回指定长度的所有书籍的库方法。

请注意,我已经删除了很多从这个,只是它不是5页长!我通常会有几种不同类型的书籍,比如作者等,但希望从下面你能很好地了解这个结构。

说到结构,我只想知道我的结构是否好。还有一个问题,如果你从图书馆里删除一本书,它仍然会出现在我创建的任何阅读列表中。我不知道是否有办法处理这件事,或者我是不是想错了。

代码语言:javascript
复制
class Library:
    
    def __init__(self, books = None):
        if books is None:
            self.books = []
        else:
            self.books = books

    def add_book(self, book):
        self.books.append(book)
        
    def remove_book(self, book):
        self.books.remove(book)
        
    def pages_over_n(self, number):
        """Returns books that have more than specified number of pages"""
        return [book for book in self.books if book.pages > number]
    
    def get_library(self):
        """Returns all books in Library"""
        return self.books
    
    def __repr__(self):
        return f"Library({self.books})"
    
    def __str__(self):
        return f"Library has {len(self.books)}"

class Novel:
    
    def __init__(self, title, pages, publicationDate):
        self.title = title
        self.pages = pages
        self.publicationDate = publicationDate
        
    def __repr__(self):
        return f"Novel({self.title}, {self.pages}, {self.publicationDate})"
    
    def __str__(self):
        return f"Media: {self.title}"
    
class ComicBook(Novel):
    
    def __init__(self, title, pages, publicationDate, artist):
        super().__init__(title, pages, publicationDate)
        self.artist = artist
        
    def get_artist(self):
        return f"Artist: {self.artist}"
        
    def __repr__(self):
        return (f"ComicBook({self.title}, {self.pages}, " 
                f"{self.publicationDate}, {self.artist})")
    
class ReadingList:
    
    def __init__(self):
        self.list = []
    
    def add_book(self, book):
        """Add book to reading list"""
        self.list.append(book)
        
    def remove_book(self, book):
        """Remove book from reading list"""
        self.list.remove(book)

    def total_pages(self):
        """Returns total pages in reading list"""
        total = 0
        for book in self.list:
            total += book.pages
        return total

    def __repr__(self):
        return f"ReadingList({self.list})"
    
    def __str__(self):
        return f"Reading list of size {len(self.list)}"

# Initialise Library
library = Library()

# Create a few books
novel1 = Novel("Harry Potter", 500, 1991)
novel2 = Novel("LotR", 1000, 1960)
novel3 = Novel("Game of Thrones", 2000, 2018)
comic1 = ComicBook("Batman", 100, 2020, "Stan Lee")

# Add books to library.
library.add_book(novel1)
library.add_book(novel2)
library.add_book(novel3)
library.add_book(comic1)

# Create a new reading list.
readingListOne = ReadingList()

# Add a couple of books to reading list.
readingListOne.add_book(novel1)
readingListOne.add_book(comic1)
EN

回答 3

Code Review用户

回答已采纳

发布于 2020-10-09 11:01:23

一般观测

我在这里是百分之百诚实的,当我只是看一下你的代码,没有什么是立即令人恐惧的。

  • 您不应该使用已经在Python中定义的变量/函数名。类ReadingList有一个属性list。这是个坏主意,因为已经有一个list关键字可以与您的定义发生冲突。

码结构

您已经尝试过实现继承的概念,尽管它很好,但是将基类作为Book会好得多。

这更有意义,因为小说和漫画书are类型的书籍。因此可以共享属性和方法。

您的Book类可以具有如下的属性和方法

  • 作者
  • 名字
  • 在所有类型的书中常见的东西

您的子类Novel可以

  • 小说中的特殊之处

一个非常基本的实现

代码语言:javascript
复制
class Book():
    def __init__(self,author,name):
        self.author = author
        self.name = name

    def print_book(self):
        print(f"{self.name} by {self.author}")




class Novel(Book):
    def __init__(self,author,name):
        Book.__init__(self,author,name)

    def display(self):
        print(f"The Novel {self.name}")
        # Novel stuff which isn't common in all books

class Comic(Book):
    def __init__(self,author,name):
        Book.__init__(self,author,name)

    def display(self):
        print(f"The comic book {self.name}")
        # Comic book stuff which isn't common in all books
        

nov = Novel("J.K Rowling","Harry Potter")
nov.print_book()

关键是,您的Novel类基本上是一个作为作者、页面等的Book,但它也有其他书籍(如Comic )不需要的东西。这样您就不需要创建一个全新的类了,但是您可以从Book类继承公共部分,并在Novel中添加额外的内容。

创建库

如果您愿意,可以在这里停止,并将您的Book类视为库。但是,如果您想变得更疯狂,您可以使用另一个名为Magazine的类,这将是父Library的子类。这就是它的样子。

但是这会变得非常复杂,而且,从Library继承D40也没有多大意义。因此,保持Library作为一个单独的、只控制账簿的做法是个好主意。

代码语言:javascript
复制
class Library():
    def __init__(self,book_list = []):
        self.book_list = book_list

    def new_book(self,book):
        self.book_list.append(book)

    def remove_book(self,name): # book is the name of the book
        for book in self.book_list:
            if book.name == name:
                self.book_list.remove(book)
                break


    def print_books(self):
        for book in self.book_list:
            print(f"Book {book.name} by {book.author}")


class Book():
    def __init__(self,author,name):
        self.author = author
        self.name = name

    def print_book(self):
        print(f"{self.name} by {self.author}")




class Novel(Book):
    def __init__(self,author,name):
        Book.__init__(self,author,name)

    def display(self):
        print(f"The Novel {self.name}")
        # Novel stuff which isn't common in all books

class Comic(Book):
    def __init__(self,author,name):
        Book.__init__(self,author,name)

    def display(self):
        print(f"The comic book {self.name}")
        # Comic book stuff which isn't common in all books


my_lib = Library()
my_lib.new_book(Novel("J.K Rowling","Harry Potter"))
my_lib.new_book(Novel("Aryan Parekh","Stack"))

my_lib.remove_book("Stack")

my_lib.print_books()
票数 8
EN

Code Review用户

发布于 2020-10-09 11:01:41

简化了class Library

的构造函数

参数None的默认值为books,但随后立即将该None转换为空列表。为什么不使用[]作为默认值呢?

代码语言:javascript
复制
class Library:
    def __init__(self, books = []):
        self.books = list(books)

    ...

注意,为了防止ojdo提到的可变默认参数问题,您应该创建初始列表的副本。

是一致的

您可以在class Library的构造函数中使用一个可选的图书列表,但是在class ReadingList中没有这样的东西。这有点令人吃惊。

也要和命名的东西保持一致。在class Library中,图书列表称为books,但在class ReadlingList中,您将其命名为list。在这两种情况下,我都把它命名为books

还应与职能名称保持一致:

  • get_library()应该是get_books(),因为它只返回图书列表,而不是Library类型的对象。
  • pages_over_n()应该是get_books_with_over_n_pages()或类似的东西,以表明它返回的是Books,而不是页面。
  • total_pages()应该改名为get_total_pages()calculate_total_pages()。喜欢用动词作为函数名,用名词表示变量。

我也想知道为什么ComicBookartist,而Novel没有author?为什么会有get_artist()而没有get_title()get_publication_date()等等?使一切保持一致将使您的类的使用更加容易。

中移除图书

正如您注意到的,您可以从图书馆中删除一本书,但它不会从任何阅读列表中删除。但这没什么。如果你的图书馆删除了一本书,为什么一本书会神奇地从现实生活的阅读列表中消失呢?请注意,您的代码是非常好的,Python实际上不会从内存中删除Book对象,直到对它的最后引用消失。

但是,如果您希望一本书在从ReadingList中删除时从Library中消失,那么这两个对象之间需要进行某种通信。有两种选择:

  1. A Library保存了一张引用其Books的ReadingLists的列表,当您在Library上调用remove_book()时,它将遍历所有的ReadingListD54,并在它们上调用remove_book()
  2. ReadingList保存对Library的引用,其Books包含该引用。每当调用访问阅读列表上图书列表的成员函数时,该成员函数首先必须过滤掉Library中不再存在的所有书籍。
票数 8
EN

Code Review用户

发布于 2020-10-09 12:37:19

由于LibraryReadingList只是不同类型的书籍列表,所以我选择将泛型BookList定义为LibraryReadingList都继承的基类。

这样做可以避免重复定义如下所示的常见方法,这两个类都已经这样做了。下面是来自Library类的一些方法。

代码语言:javascript
复制
def __init__(self, books = None):
    if books is None:
        self.books = []
    else:
        self.books = books

def add_book(self, book):
    self.books.append(book)
    
def remove_book(self, book):
    self.books.remove(book)

下面是来自ReadingList类的类似方法。

代码语言:javascript
复制
def __init__(self):
    self.list = []

def add_book(self, book):
    """Add book to reading list"""
    self.list.append(book)
    
def remove_book(self, book):
    """Remove book from reading list"""
    self.list.remove(book)

正如G. Sliepen所说,您应该简化Library构造函数以匹配ReadingList构造函数。这样做之后,方法是完全相同的。

通过定义泛型图书列表类,您不仅不必不断地重新定义公共功能,还可以利用多态函数。

例如,不必让每个类负责输出其本身的字符串表示形式,您可以定义一个BookListHTMLWriter,它接受图书列表项(无论是ReadingList还是Library,如果它们都是从同一个BookList对象派生出来的,两者都很好),并输出一个D15表表示。

同样,您也可以定义一个BookListXMLWriter,它输出BookList对象或其子类的XML表示。您可以在此之前进一步进行OOP重构,重构BookListHTMLWriterBookListXMLWriter类所共有的功能,并从基类BookListWriter基类中派生它们。

乐观地说,面向对象的编程可以是一种后天养成的兴趣(而且是深奥的),但是一旦你开始用这些术语思考,它就是一个有用的心理模型,即使它不是你唯一的。或者你更喜欢一个。

关于你如何把一本书从你的图书馆里删除而不是从你的阅读清单中删除的观点(S),我同意之前的回答,这是有意义的。我认为你提到的问题可能是引用完整性的问题,我也注意到了。

显然,您已经注意到LibraryReadingList都是Books的列表。当阅读代码时,我总是开始描绘相应的数据库模式,我认为您所指的问题是,应该有一个已经创建的图书的主列表,您可以从中添加到个人图书馆和/或阅读列表中,这可能取决于您是否拥有这本书。我将这个主列表称为“目录”,或者用来区分这个主列表和属于特定用户的特定类型的列表的东西。

此外,这个目录只是数据库中的books表中的所有记录。

要实现该程序的数据库,您可以首先创建booksusers表,然后创建仅由用户id和图书id组成的第三个表。在此链接表中查询给定的用户id将为您提供这个人拥有的所有书籍。

如果您希望用户能够创建任意库,而不仅仅是基于他们是否拥有一本书,那么您只需要创建一个以用户id作为字段的reading_lists表来指示所有者,然后使用列表id和图书id创建另一个链接表。

这将通过防止删除图书记录来解决引用完整性问题,除非在ON DELETE表架构中指定适当的books操作。

Books子类划分为Novels和Magazine want会使事情复杂化,因为如果继承层次结构在子类上注入了足够复杂的差异,那么对象就不再很好地映射到单个表中(假设您想要一个可合理规范化的模式)。

如果您达到这一点,一个好的实体-关系映射工具可以节省您的时间沮丧。

票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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