Biblioteca de libros personal
Muy nuevo en OOP con Python, y siento que todavía no entiendo totalmente OOP. Sin embargo, pensé en armar una biblioteca personal de libros en línea para practicar la herencia, los métodos, las cadenas de documentos, etc.
El programa le permite a uno agregar libros (cómics o novelas, con los cómics heredados de las novelas) a la propia biblioteca y crear listas de lectura personalizadas.
Hay algunos métodos diferentes. Por ejemplo, un método de biblioteca que devolverá todos los libros de una longitud determinada.
Tenga en cuenta que he eliminado bastante de esto, ¡solo que no tiene 5 páginas! Normalmente tendría varios tipos diferentes de libros, atributos como Autor, etc. Pero espero que de lo que sigue a continuación se haga una buena idea de la estructura.
Hablando de estructura, me gustaría saber si la mía es buena. Además, existe un problema por el cual, si elimina un libro de la biblioteca, seguirá apareciendo en las listas de lectura que he creado. No sé si hay una manera de lidiar con esto, o si estoy pensando en esto de la manera incorrecta.
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)
Respuestas
Observaciones generales
Estoy siendo 100% honesto aquí, cuando le doy un vistazo a su código, no hay nada que sea inmediatamente horrible con él.
- No debe usar nombres de variables / funciones que ya estén definidos en Python. Tu clase
ReadingListtiene un atributolist. Esta es una mala idea ya que existe unalistpalabra clave que puede entrar en conflicto con su definición.
Estructura de código
Ha intentado implementar la idea de herencia, aunque está bien, será mucho mejor tener su clase base como Book.
Esto tiene más sentido debido al hecho de que las novelas y los cómics son tipos de libros . Y, por tanto, puede compartir atributos y métodos.
Tu Bookclase puede tener atributos y métodos como
- Autor
- Nombre
- Cosas que son comunes en todo tipo de libros.
Y su clase infantil Novelpuede tener
- Sutff que es especial en la novela
Una implementación muy básica
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()
El punto es que tu Novelclase es básicamente una Bookque como autor, páginas, etc. Pero también tiene algo que otros libros como Comicno tienen. De esta manera, no necesita crear una clase completamente nueva, sino que simplemente puede heredar el común de la Bookclase y agregar las cosas adicionales enNovel
Creando una biblioteca
Si lo desea, puede detenerse aquí y considerar su Bookclase como una biblioteca. Pero si quiere volverse más loco, puede llamar a otra clase Magaziney esa sería la clase secundaria del padre Library. Así es como se vería.
Pero esto sería ser muy complicado, por otra parte, no tendría mucho sentido para heredar Booka partir Library. Por lo tanto, es una buena idea mantener Librarycomo un elemento separado que simplemente controlará los libros.
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()
Simplifica el constructor de class Library
Tiene un valor predeterminado de Nonepara el parámetro books, pero luego procede inmediatamente a convertirlo Noneen una lista vacía. ¿Por qué no utilizarlo []como valor predeterminado?
class Library:
def __init__(self, books = []):
self.books = list(books)
...
Tenga en cuenta que para evitar el problema de los argumentos predeterminados mutables como lo menciona ojdo, debe hacer una copia de la lista inicial.
Se consistente
Toma una lista opcional de libros en el constructor de class Library, pero no existe tal cosa en class ReadingList. Eso es un poco sorprendente.
También sea consistente con nombrar las cosas. En class Library, se llama a la lista de libros books, pero en class ReadlingListusted lo nombra list. Lo nombraría booksen ambos casos.
También sea coherente con los nombres de las funciones:
get_library()debería serget_books(), ya que solo devuelve la lista de libros, no un objeto de tipoLibrary.pages_over_n()debería serget_books_with_over_n_pages()o algo similar, para indicar que devuelveBooks, no páginas.total_pages()debe cambiarse el nombreget_total_pages()ocalculate_total_pages(). Prefiere usar verbos para nombres de funciones y sustantivos para variables.
Y también me pregunto por qué a ComicBooktiene un artist, pero a Novelno tiene author. Y por qué hay una get_artist(), pero no get_title(), get_publication_date()y así sucesivamente? Hacer que todo sea coherente facilitará mucho el uso de sus clases.
Eliminar libros de la biblioteca
Como notó, puede eliminar un libro de la biblioteca, pero no se eliminará de ninguna lista de lectura. Pero eso está bien. ¿Por qué un libro desaparecería mágicamente de una lista de lectura de la vida real si su biblioteca elimina ese libro? Tenga en cuenta que su código está perfectamente bien, Python en realidad no eliminará el Bookobjeto de la memoria hasta que desaparezca la última referencia.
Sin embargo, si desea que un libro desaparezca del ReadingListmomento en que se quita del Library, entonces de alguna manera debe haber alguna comunicación entre esos dos objetos. Hay dos opciones:
A
Librarymantiene una lista deReadingLists que hacen referencia a susBooks, y cuando llamaremove_book()a aLibrary, a su vez iterará sobre todos losReadingLists y los llamaráremove_book()también.A
ReadingListguarda una referencia aLibrarycuyoBooks contiene. Siempre que llame a una función miembro que acceda a la lista de libros en la lista de lectura, esta función miembro primero tiene que filtrar todos los libros que ya no están en elLibrary.
Dado que Libraryy ReadingListson solo diferentes tipos de listas de libros, optaría por definir un genérico BookListcomo una clase base de la que heredan Libraryy ReadingListambos.
Hacer esto le evita la repetición de tener que definir métodos comunes como el siguiente, que ya hacen sus dos clases. A continuación se muestran algunos métodos de la Libraryclase.
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)
Y aquí están los métodos análogos de la ReadingListclase.
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)
Como dijo G. Sliepen , debe simplificar el Libraryconstructor para que coincida con el ReadingListconstructor. Después de hacer esto, los métodos son exactamente los mismos.
Al definir una clase de lista de libros genérica, no solo no tiene que seguir redefiniendo la funcionalidad común, sino que también puede aprovechar las funciones polimórficas.
Por ejemplo, en lugar de hacer que cada clase sea responsable de generar una representación de cadena de sí misma, podría definir un BookListHTMLWriterque tome un elemento de la lista de libros (ya sea a ReadingListo a Library, ambos están bien cuando se derivan del mismo BookListobjeto) y genera un Representación de tabla HTML.
Del mismo modo, también puede definir un BookListXMLWriterque genere la representación XML de un BookListobjeto o una subclase del mismo. Se podría aprovechar esta POO-refactorización aún más mediante la continuación de la refactorización común funcionalidad a los BookListHTMLWritery las BookListXMLWriterclases y los derivar de una base de BookListWriterclase base.
De manera optimista, la programación orientada a objetos puede ser un gusto adquirido (y esotérico, además), pero una vez que empiezas a pensar en estos términos, es un modelo mental útil, incluso si no es el único. O preferiste uno, incluso.
Con respecto a su punto sobre cómo eliminar un libro de su biblioteca no lo elimina de su (s) lista (s) de lectura, estoy de acuerdo con las respuestas anteriores en que esto tiene sentido. Creo que el problema al que te refieres podría ser la cuestión de la integridad referencial, que yo también noté.
Obviamente ya lo ha notado Libraryy ReadingListambas son listas de Books. Cuando leo el código, siempre comienzo a imaginarme el esquema de la base de datos correspondiente, y el problema al que creo que te refieres es que debería haber una lista principal de libros ya creados desde los que puedes agregar a una biblioteca personal y / o lista de lectura , presumiblemente dependiendo de si eres el propietario del libro. Yo llamaría a esta lista principal un "catálogo" o algo para diferenciar esta lista principal de una lista de un tipo particular que pertenece a un usuario en particular.
Además, este catálogo sería simplemente todos los registros en una bookstabla en su base de datos.
Para implementar una base de datos para este programa, puede comenzar creando las tablas booksy users, y luego creando una tercera tabla que consta solo de la identificación de un usuario y la identificación de un libro. Al consultar esta tabla de vínculos para un ID de usuario determinado, obtendría todos los libros que posee esta persona.
Si desea que los usuarios puedan crear bibliotecas arbitrarias basándose no solo en si poseen un libro, solo necesitaría crear una reading_liststabla con una identificación de usuario como campo para indicar el propietario, y luego crear otra tabla de enlaces usando una lista id y un id de libro.
Esto resolvería su problema de integridad referencial al evitar la eliminación de un registro de libro a menos que especifique una ON DELETEacción apropiada en el booksesquema de la tabla.
Subclasificar Books en Novelsys Magazinecomplica las cosas porque si la jerarquía de herencia imbuye diferencias suficientemente complejas en sus subclases, sus objetos ya no se mapean bien en tablas individuales (asumiendo que desea un esquema razonablemente normalizable).
Si llega a este punto, una buena herramienta de mapeo relacional de entidades puede ahorrarle horas de frustración.