El polimorfismo en Python se vuelve más claro y potente cuando se combina con typing y Protocol. Con contratos flexibles y sin herencia estricta, cualquier objeto que implemente los métodos requeridos puede participar, manteniendo la flexibilidad del duck typing y ganando ayuda de tipado estático en el editor.
¿Cómo potencia Protocol el polimorfismo en Python?
El polimorfismo permite que diferentes objetos respondan al mismo mensaje con resultados distintos: el método de solicitar libro funciona diferente si lo llama un estudiante o un profesor. Con Protocol, se define un contrato que exige la presencia de ese método en cualquier objeto que queramos usar en una colección común.
Mismo mensaje, diferentes resultados al invocar solicitar libro.
Interfaces sin herencia: basta con cumplir el contrato del Protocol.
Tipado estático que ayuda: el editor alerta si un objeto no implementa el método requerido.
¿Qué papel cumple el duck typing?
Python aplica duck typing: si un objeto “camina y hace cuak como un pato”, se acepta. No importa la clase, solo que tenga los métodos necesarios. Protocol formaliza ese principio con contratos verificables por herramientas de tipos, sin perder flexibilidad.
¿Cómo definir un contrato con typing.Protocol?
Se importa Protocol desde typing para crear una clase que describe la interfaz mínima. Aquí, el contrato exige el método solicitar_libro con firma clara: recibe un título como str y retorna un str. El cuerpo usa ... para indicar que no hay implementación.
from typing import Protocol
classSolicitanteProtocol(Protocol):defsolicitar_libro(self, titulo:str)->str:"""Retorna el resultado de la solicitud de préstamo."""...
Hereda de typing.Protocol para declarar el contrato.
Define la firma con tipos: título como str y retorno str.
Usa elipsis (...) como cuerpo, solo describe la intención.
Sirve para cualquier subclase o nuevo objeto “usuario” que deba solicitar libros.
¿Cómo obligar a implementar el método?
Al tipar colecciones, el editor valida que cada elemento cumpla el contrato. Si se intenta agregar un objeto que no define solicitar_libro, se marca como error de tipos.
classLibro:def__init__(self, titulo:str, autor:str, isbn:str)->None: self.titulo = titulo
self.autor = autor
self.isbn = isbn
# Lista tipada: solo admite elementos que cumplan SolicitanteProtocolusuarios:list[SolicitanteProtocol]=[]usuarios.append(Libro("Título de prueba","Autor de prueba","ISBN"))# error en el editor: no implementa solicitar_libro
¿Cómo usar una lista tipada e iterarla?
Se crea una lista de usuarios que cumplen el contrato y se itera. Cada elemento ejecuta su propia versión de solicitar_libro, aplicando polimorfismo “puro” y retornando resultados específicos.
classEstudiante:defsolicitar_libro(self, titulo:str)->str:returnf"Préstamo autorizado para estudiante: {titulo}"classProfesor:defsolicitar_libro(self, titulo:str)->str:returnf"Préstamo autorizado para profesor: {titulo}"usuarios:list[SolicitanteProtocol]=[Estudiante(), Profesor()]for usuario in usuarios:print(usuario.solicitar_libro("Título de prueba"))
Se ejecuta la versión adecuada de solicitar_libro según el objeto.
Se imprime el resultado del préstamo para cada usuario.
Si se agrega un objeto sin el método, el editor lo marca como incompatible con el Protocol.
¿Qué reto práctico puedes implementar ya?
Define un Protocol para libros y crea dos implementaciones con su propia lógica de préstamo. La idea: contratos claros, implementaciones independientes, lista tipada y polimorfismo en acción.
from typing import Protocol
classLibroProtocol(Protocol):defprestar(self):...defcalcular_duracion(self):...classLibroFisico:defprestar(self):...defcalcular_duracion(self):...classLibroElectronico:defprestar(self):...defcalcular_duracion(self):...biblioteca:list[LibroProtocol]=[LibroFisico(), LibroElectronico()]for libro in biblioteca:# Usa la misma interfaz con comportamientos distintosprint(libro.prestar(), libro.calcular_duracion())
¿Te gustaría compartir cómo aplicarías Protocol en tus modelos de dominio o qué métodos incluirías en tu contrato de libro? Deja tus ideas y preguntas en los comentarios.
from typing importProtocolclassLibroProtocol(Protocol): def prestar_libro(self)-> str:"""
Método que debe implementar cualquier clase que quiera prestar un libro
Returns:str:Un mensaje indicando que el libro ha sido prestado
"""
def duracion_prestamo(self)-> int:"""
Método que debe implementar cualquier clase que quiera calcular la duración de un préstamo de un libro
Returns:int:La duración del préstamo en días"""
# ClaseLibroclassLibro: # Método constructor
def __init__(self, titulo, autor, isbn, disponible =True): self.titulo= titulo
self.autor= autor
self.isbn= isbn
self.disponible= disponible
#Atributo privado
self.__prestamos=0 # Lista para guardar los préstamos del libro
# Método para imprimir la representación escrita del libro
def __str__(self):return f"Titulo: {self.titulo} - Autor: {self.autor} - ISBN: {self.isbn} - Disponible: {self.disponible}" # Método para cambiar la disponibilidad del libro
def prestar_libro(self):if self.disponible: self.disponible=False self.__prestamos+=1 # Registrar el préstamo en la lista
return f"El libro *{self.titulo} ha sido prestado"return f"El libro *{self.titulo} no está disponible" def devolver_libro(self):if self.disponible==False: self.disponible=Truereturn f"El libro *{self.titulo} ha sido devuelto" def es_popular(self): # RetornaTrue si el libro ha sido prestado más de 5 veces
if self.__prestamos>5:return f"El libro *{self.titulo} es popular, ha sido prestado {self.__prestamos} veces"return f"El libro *{self.titulo} no es popular, ha sido prestado {self.__prestamos} veces" #Método para acceder al atributo privado
def get_prestamos(self):return self.__prestamos #Método para cambiar el atributo privado
def set_prestamos(self, prestamos): self.__prestamos= prestamos
classLibroFisico(Libro): def __init__(self, titulo, autor, isbn, disponible =True,dias_en_prestamo: int =0):super().__init__(titulo, autor, isbn, disponible) self.dias_en_prestamo= dias_en_prestamo
def prestar_libro(self):if self.disponible: self.disponible=False self.set_prestamos(self.get_prestamos()+1) # Incrementa el número de préstamos
return f"El libro físico *{self.titulo} ha sido prestado"return f"El libro físico *{self.titulo} no está disponible" def duracion_prestamo(self)-> int:return f"La duración del préstamo del libro físico *{self.titulo} es de {self.dias_en_prestamo} días"classLibroDigital(Libro): def __init__(self, titulo, autor, isbn, disponible =True,dias_en_prestamo: int =0):super().__init__(titulo, autor, isbn, disponible) self.dias_en_prestamo= dias_en_prestamo
def prestar_libro(self):if self.disponible: self.disponible=False self.set_prestamos(self.get_prestamos()+1) # Incrementa el número de préstamos
return f"El libro digital *{self.titulo} ha sido prestado"return f"El libro digital *{self.titulo} no está disponible" def duracion_prestamo(self)-> int:return f"La duración del préstamo del libro digital *{self.titulo} es de {self.dias_en_prestamo} días"# Instancia de la clase Librolibro1 =Libro("El principito","Antoine de Saint-Exupéry","1234567890",True)libro2 =Libro("1984","George Orwell","1234567890",True)libro3 =LibroFisico("El principito","Antoine de Saint-Exupéry","1234567890",True,10)libro4 =LibroDigital("1984","George Orwell","1234567890",True,10)# Lista de libros
listaCatalogo =[libro1, libro2, libro3, libro4]# Imprimir el catálogo de libros
print(f"Catálogo de libros:")for libro inlistaCatalogo:print(libro)#print(libro1.__prestamos) # No se puede acceder al atributo privado
libro2.set_prestamos(100) # Cambia el valor del atributo privado
print(libro2.get_prestamos()) # Accede al atributo privado
print("================================================")print(libro1.prestar_libro())print(libro1.devolver_libro())print(libro1.prestar_libro())print(libro1.devolver_libro())print(libro1.prestar_libro())print(libro1.devolver_libro())print(libro1.prestar_libro())print(libro1.devolver_libro())print(libro1.prestar_libro())print(libro1.devolver_libro())print(libro1.prestar_libro())print(libro1.es_popular())print(libro2.es_popular())print("================================================")libro3.dias_en_prestamo=15print(libro3.prestar_libro())print(libro3.duracion_prestamo())libro4.dias_en_prestamo=8print(libro4.prestar_libro())print(libro4.duracion_prestamo())print("================================================")
🧠POLIMORFISMO Y PROTOCOL EN PYTHON
🚀 Idea Central
El polimorfismo permite que distintos objetos respondan al mismo mensaje de forma diferente.
En Python, su poder crece al combinarlo con typing.Protocol:
print(usuario.solicitar_libro("Título de prueba"))
🔹 Qué ocurre:
Cada objeto ejecuta su propia versión de solicitar_libro.
El mismo mensaje → distintos resultados.
Si un objeto no cumple el contrato, el editor lo marca como error.
# ============================================================================# PROTOCOL PARA LIBROS - Polimorfismo basado en contratos claros# ============================================================================# Un Protocol define un contrato que cualquier clase puede implementar# sin necesidad de herencia. De esta forma, LibroFisico y LibroElectronico# son independientes pero cumplen el mismo contrato (LibroProtocol).classLibroProtocol(Protocol):"""Define el contrato que deben cumplir todos los tipos de libros."""defprestar(self)->str:"""Retorna un mensaje de autorización de préstamo específico del tipo."""...defcalcular_duracion(self)->str:"""Retorna la duración del préstamo según el tipo de libro."""...classLibroFisico:"""Implementación de un libro físico con su propia lógica de préstamo.
Los libros físicos tienen una duración estándar de 14 días y requieren devolución física. No hereda de ninguna clase base, pero implementa el contrato LibroProtocol.
"""def__init__(self, titulo:str, autor:str): self.titulo = titulo
self.autor = autor
defprestar(self)->str:"""Lógica de préstamo para libros físicos: se registra en el sistema."""returnf"Libro físico '{self.titulo}' de {self.autor} prestado. Por favor, cuídalo bien."defcalcular_duracion(self)->str:"""Los libros físicos se prestan por 14 días."""returnf"Duración del préstamo: 14 días (debe ser devuelto en perfectas condiciones)."classLibroElectronico:"""Implementación de un libro electrónico con su propia lógica de préstamo.
Los libros electrónicos operan diferente: no se pierden, pueden ser copiados, y tienen acceso digital. No hereda de ninguna clase base, pero implementa el contrato LibroProtocol.
"""def__init__(self, titulo:str, autor:str, formato:str): self.titulo = titulo
self.autor = autor
self.formato = formato # PDF, EPUB, MOBI, etc.defprestar(self)->str:"""Lógica de préstamo para libros electrónicos: acceso digital inmediato."""returnf"Libro electrónico '{self.titulo}' de {self.autor} ({self.formato}) - Acceso digital autorizado instantáneamente."defcalcular_duracion(self)->str:"""Los libros electrónicos se prestan por 30 días."""returnf"Duración del acceso: 30 días (acceso digital ilimitado durante este período)."# ============================================================================# DEMOSTRACIÓN DE POLIMORFISMO CON LISTA TIPADA# ============================================================================# A pesar de que LibroFisico y LibroElectronico son clases independientes,# pueden convivir en la misma lista tipada gracias al Protocol.# El método prestar() y calcular_duracion() se comportan distintamente# según el tipo concreto, demostrando polimorfismo en acción.print("\n"+"="*70)print("SISTEMA DE GESTIÓN DE BIBLIOTECA CON PROTOCOLS")print("="*70)# Crear instancias de diferentes tipos de libroslibro_fisico_1 = LibroFisico("Cien años de soledad","Gabriel García Márquez")libro_fisico_2 = LibroFisico("Don Quijote","Miguel de Cervantes")libro_electronico_1 = LibroElectronico("Clean Code","Robert C. Martin","PDF")libro_electronico_2 = LibroElectronico("Python Avanzado","Guido van Rossum","EPUB")# Lista tipada que acepta cualquier objeto que implemente LibroProtocolbiblioteca:list[LibroProtocol]=[ libro_fisico_1, libro_fisico_2, libro_electronico_1, libro_electronico_2
]# Iterar sobre la biblioteca: mismo interface, comportamientos distintosprint("\n📖 Procesando solicitudes de préstamo en la biblioteca:\n")for libro in biblioteca:print(f"{libro.prestar()}")print(f" {libro.calcular_duracion()}\n")print("="*70)print("✅ Polimorfismo en acción: LibroFisico y LibroElectronico implementan")print(" el mismo Protocol (LibroProtocol) pero con lógicas diferentes.")print("="*70)
Hola, al darle el tipo a la lista de usuarios, el editor VS Code no me mostró ningún error. ¿Será que me falta una extensión?
Revisa que tengas instalada: mypy en vscode.
Gracias profe!
Dejo capturas de las modificaciones a la clase libro para el reto
Buen trabajo
Aqui mi solución, lo importante para mi fue clave entender que un Protocol se basa en polimorfismo estructural: si un objeto tiene el método con la firma correcta, vale!, aunque no herede del Protocol directamente,
Es útil recordar que, en otros lenguajes (como Java o C#) eso se llama interface: una especie de contrato que dice qué métodos debe tener una clase, aunque no dice cómo se implementan.
from typing import Protocol
classLibroProtocol(Protocol):defprestar(self)->str:"""Metodo que debe implementar cualquier libro para ser prestado."""...defcalcular_duracion_prestamo(self, dias:int)->int:"""Metodo que debe implementar cualquier libro para calcular la duración del préstamo."""...classLibro:def__init__( self, titulo, autor, isbn, disponible=True,): self.titulo = titulo
self.autor = autor
self.isbn = isbn
self.disponible = disponible
self.__prestamos =0def__str__(self):returnf"{self.titulo} por {self.autor} (ISBN: {self.isbn}) - {'Disponible'if self.disponible else'No Disponible'}"defprestar(self)->str:if self.disponible: self.disponible =False self.__prestamos +=1returnf"{self.titulo} ha sido prestado."returnf"{self.titulo} no está disponible para préstamo."defdevolver(self): self.disponible =Truereturnf"{self.titulo} ha sido devuelto."defes_popular(self):# Método retorna TRUE si un libro es popular cuando ha sido prestado más de 5 vecesreturn self.__prestamos >5defget_veces_prestado(self):return self.__prestamos
defset_veces_prestado(self, veces):if veces >=0: self.__prestamos = veces
else:raise ValueError("El número de préstamos no puede ser negativo.")defcalcular_duracion_prestamo(self, dias:int)->int:# Método que calcula la duración del préstamo en díasreturn dias
classLibroFisico(Libro):# aqui el limite mmaximo 10 diasdef__init__(self, titulo, autor, isbn, disponible=True):super().__init__(titulo, autor, isbn, disponible)defcalcular_duracion_prestamo(self, dias)->int:# Método que calcula la duración del préstamo en díasreturnmax((dias,10))classLibroDigital(Libro):# aqui el limite maximo debe ser 5 diasdef__init__(self, titulo, autor, isbn, disponible=True):super().__init__(titulo, autor, isbn, disponible)defcalcular_duracion_prestamo(self, dias)->int:# Método que calcula la duración del préstamo en díasreturnmax((dias,5))# Ejemplo de usomi_libro = Libro("Cien Años de Soledad","Gabriel García Márquez","978-3-16-148410-0")otro_libro = Libro("1984","George Orwell","978-0-452-28423-4",True)defimprimir_prestamos(items:list[LibroProtocol])->None:for item in items:print(item.prestar(),"Duración:", item.calcular_duracion_prestamo(20),"días")imprimir_prestamos([mi_libro, otro_libro])print(mi_libro.prestar())print(mi_libro.devolver())print(mi_libro.prestar())print(mi_libro.get_veces_prestado())
Me podrian compartir cual es el curso anterior en la ruta?
Creo que seria este, o por lo menos eso parece por los títulos de las clases:
from libro importBookclassUser(): def __init__(self,name,code,email,password): self.name= name
self.code= code
self.email= email
self.__password= password
self.requested_books=[] def request(self,title):return f"{self.name}Requested {title} successfully" def get_back(self,title):for book in self.requested_books:if book == title: self.requested_books.remove(book)return f"{self.name} returned the book {title}"return f"{self.name} doesn't have the book {title}" def set_password(self,password): self.__password= password
def get_password(self):return self.__passwordclassStudent(User): def __init__(self, name, code, email, password,career):super().__init__(name, code, email, password) self.career= career
self.book_limit=3 def request(self,title):iflen(self.requested_books)< self.book_limit: self.requested_books.append(title)return f"{self.name} Requested {title} successfully"else:return f"{self.name}'s Request denied, book limit achaived: {self.book_limit}"classProfessor(User): def __init__(self, name, code, email, password):super().__init__(name, code, email, password) self.book_limit=None # def request(self,title): # return f"{self.name} Requested {title} successfully"cano =Professor("Samuel","FCT087609","samuelcano@ugb.edu.sv","sysadmin")carlos =Student("Carlos","SMSS061022","cemluna2021@gmail.com","carlos1234","System & Network Engineer")print(cano.request("OWASP TOP 10 2025"))print(carlos.request("Deployments and architecture software"))print(carlos.request("Backend with python"))print(carlos.request("Burn your Torch"))print(carlos.request("How to Win the Premier League"))print(carlos.get_back("Burn your Torch"), carlos.requested_books)
from typing import Protocol
classBookProtocol(Protocol):"""Protocol to implement in all book classes"""deflend_book(self):"""Method to perform lend_book"""...defset_duration(self, days:int):"""Method to perform loan duration"""...classBook(BookProtocol):def__init__( self, tittle:str, autor:str, isbn:str, aviable:bool, loans:int=0):"""constructor
Args:
tittle (str): boock tittle
autor (str): writer
isbn (str): code
aviable (bool): is avaiable
""" self.tittle = tittle
self.autor = autor
self.isbn = isbn
self.aviable = aviable
self.__loans = loans # __ encapsula la variable a privada para evitar su acceso desde otro sistemadefget_loans(self):return self.__loans
defset_loans(self, loans): self.__loans = loans
def__str__(self):returnf"Tittle: {self.tittle}, Author: {self.autor}, ISBN: {self.isbn}"defpopolarity(self):if self.__loans >5:print(f"the {self.tittle} was the most popular books")deflend_book(self): self.change_book_aviable(True)defset_duration(self, days:int):returnf"{self.tittle} is going to be loaned by {days} days"defchange_book_aviable(self, aviable:bool): self.aviable = aviable
self.__loans =+1if aviable else-1 state ="aviable."if aviable else"not aviable."returnf"The book: {self.tittle} is {state}"classPhysicBook(Book):def__init__(self, tittle, autor, isbn, aviable, loans=0):super().__init__(tittle, autor, isbn, aviable, loans)deflend_book(self):passdefset_duration(self, days:int):passclassEbook(Book):def__init__(self, tittle, autor, isbn, aviable, loans=0):super().__init__(tittle, autor, isbn, aviable, loans)deflend_book(self):passdefset_duration(self, days:int):pass
De esta clase si no entendi nada mano :(
Puedo ayudarte a entender mejor, si me puedes dar un poco más de contexto de que es lo que realmente no entendiste
Gracias profesor, mi duda estaba en que no tenia claro el concepto de polimorfismo pero ya que aclare eso todo bien
**Estudiantes*from poo.herencia.usuarioimportUsuarioclassEstudiante(Usuario): def __init__(self, nombre, cedula, carrera):super().__init__(nombre, cedula) self.carrera= carrera
self.__limite_libros=3 self.libros_prestados=[] def __str__(self):return f"{self.nombre} - {self.cedula} de la carrera {self.carrera}, tienes un limite de libros de {self.__limite_libros}" def solicitar_libro(self, titulo):iflen(self.libros_prestados)>= self.__limite_libros:return f"Llegaste al limite de libros que se pueden solicitar {self.__limite_libros}"else: self.libros_prestados.append(titulo)return f"La solicitud del libro {titulo} fue registrada, puedes solicitar {self.__limite_libros - len(self.libros_prestados)} libros mas." def regresar_libro(self, titulo): index = self.libros_prestados.index(titulo) self.libros_prestados.pop(index)return f"Libro {titulo} devuelto correctamente, puedes solicitar {self.__limite_libros - len(self.libros_prestados)} libros mas."
***Profesorfrom poo.herencia.usuarioimportUsuarioclassProfesor(Usuario): def __init__(self, nombre, cedula):super().__init__(nombre, cedula) self.libros_prestados=[] def solicitar_libro(self, titulo): self.libros_prestados.append(titulo)return f"La solicitud del libro {titulo} fue registrada con exito." def regresar_libro(self, titulo): index = self.libros_prestados.index(titulo) self.libros_prestados.pop(index)return f"Libro {titulo} devuelto correctamente, puedes solicitar mas libros."
ResultadoC:\Users\jant2\PycharmProjects\curso\.venv\Scripts\python.exeC:\Users\jant2\PycharmProjects\curso\poo\main.pyLa solicitud del libro Python intermedio fue registrada, puedes solicitar 2 libros mas.La solicitud del libro PythonBasico fue registrada, puedes solicitar 1 libros mas.La solicitud del libro Python avanzado fue registrada, puedes solicitar 0 libros mas.Llegaste al limite de libros que se pueden solicitar 3LibroPythonBasico devuelto correctamente, puedes solicitar 1 libros mas.La solicitud del libro PythonDjango fue registrada, puedes solicitar 0 libros mas.Process finished with exit code 0
Muy buena solución!
Yo no se si alguien se cuestiono porque se devolveria un libro digital
Puede que ese no se tenga de devolver, pero si se quiera llevar el control de cuantas veces se ha "obtenido" uno.
Hice un atributo self.num_pages para la clase Book con encapsulamiento para calcular la duración del préstamo, y ví que no se puede usar en las clases hijas directamente. Para usarla me tocó especificar la clase padre así: self._Book__num_pages.
from typing importProtocolclassBookProtocol(Protocol):"""Métodos que debe implentar cualquier libro""" def lend_book(self)-> str:... def calculate_duration(self)-> str:...classBook: def __init__(self,title: str,author: str,isbn: str,num_pages: int,available: bool =True): self.title= title
self.author= author
self.isbn= isbn
self.__num_pages= num_pages
self.__available= available
self.__borrow_count=0 self.borrowing_period=15 self.borrowing_period= self.calculate_duration() def __str__(self)-> str: information = f"Title: {self.title}. Author: {self.author}. ISBN: {self.isbn}. {"Available" if self.__available else "Not available"}"return information
def lend_book(self)-> str:if self.__available: self.__available=False self.__borrow_count+=1 status = f"El libro '{self.title}' ha sido prestado exitosamente. Total de préstamos: {self.__borrow_count}. Tiempo del préstamo: {self.borrowing_period} días."else: status = f"El libro '{self.title}' ya se encuentra prestado."return status
def return_book(self)-> str:if not self.__available: self.__available=True status = f"El libro '{self.title}' se ha devuelto exitosamente."else: status = f"El libro '{self.title}' ya se encontraba disponible."return status
def is_popular(self)-> bool:if self.__borrow_count>5:returnTrueelse:returnFalse def get_borrow_count(self)-> int:return self.__borrow_count def set_borrow_count(self,set_count: int)-> str: self.__borrow_count= set_count
return f"Se ha establecido la cantidad de préstamos del libro '{self.title}' en >{self.__borrow_count}" def get_is_available(self)-> bool:return self.__available def set_is_available(self,available: bool)-> str: self.__available= available
status = f"EL libro {self.title} se encuentra disponible."if self.__availableelse f"El libro '{self.title}' se encuentra prestado."return status
def calculate_duration(self)-> int:return self.borrowing_periodclassEbook(Book): def __init__(self, title, author, isbn, __num_pages, available =True):super().__init__(title, author, isbn, __num_pages, available) def lend_book(self)-> str: self._Book__borrow_count+=1 status = f"Se ha habilitado el acceso al libro electrónico '{self.title}'. Total de solicitudes: {self._Book__borrow_count}. Tiempo del préstamo: {self.borrowing_period} días."return status
def calculate_duration(self)-> int:if self._Book__num_pages>150: self.borrowing_period= self.borrowing_period+(self._Book__num_pages-150)// 50 # Aumenta el período de préstamo de tal forma que por cada 50 páginas adicionales se añade un día másreturn self.borrowing_periodclassPhysicalBook(Book): def __init__(self, title, author, isbn, __num_pages, available =True):super().__init__(title, author, isbn, __num_pages, available) def lend_book(self):returnsuper().lend_book() def calculate_duration(self)-> int:if self._Book__num_pages>100: self.borrowing_period= self.borrowing_period+(self._Book__num_pages-100)// 50 * 2 # Aumenta el período de préstamo de tal forma que por cada 50 páginas adicionales se añaden dos día másreturn self.borrowing_period
Sí, esto pasa porque el Protocol obliga a que se cumplan las condiciones, si no está en el protocol no se puede usar.
from typing importProtocolclassBookProtocol(Protocol): def lend_book(self)-> str:... def calculate_duration(self)-> int:...classBook: def __init__(self, title, author, isbn, available, pages): self.title= title
self.author= author
self.isbn= isbn
self.available= available
self.__lended_count=0 self.pages= pages
self.duration=14 #this value is in days
def __str__(self):return f"Title: {self.title}, Author: {self.author}, ISBN: {self.isbn}, Available: {self.available}" def lend_book(self):if self.available: self.available=False self.__lended_count+=1return f"'{self.title}' lended and is not available"return f"Not available" def return_book(self): self.available=Truereturn f"'{self.title}' returned and available for lending" def is_popular(self):return self.__lended_count>=5 def get_landed_count(self):return self.__lended_count
#setter
def set_lended_count(self, count): self.__lended_count= count
def calculate_duration(self):return self.durationclassPhysicalBook(Book): def __init__(self, title, author, isbn, available, pages):super().__init__(title, author, isbn, available, pages) self.return_limit_days= self.calculate_duration() def lend_book(self):if self.available: self.available=False self.__lended_count+=1return f"'{self.title}' lended and you have {self.return_limit_days} days to return it"return f"Not available" def calculate_duration(self):if self.pages>100: self.duration= self.pages+ self.pages// 10returnint(self.duration)return self.durationclassEBook(Book): def __init__(self, title, author, isbn, available, pages):super().__init__(title, author, isbn, available, pages) def lend_book(self): self.available=True self.__lended_count+=1return f"E-books do not have limit days to return it" def calculate_duration(self):if self.pages>200: self.duration= self.pages+ self.pages// 5returnint(self.duration)return self.duration