Clases y métodos abstractos
Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 25 de 64.
Las clases abstractas son clases que no se pueden instanciar directamente y contienen métodos abstractos que deben implementar las subclases.
Piensa en ello como si fuera un formulario: Una clase abstracta es como un formulario obligatorio en el que algunos campos son obligatorios (métodos abstractos) y otros son opcionales (métodos concretos). No puedes enviar el formulario en sí; primero debes rellenar todos los campos obligatorios. Del mismo modo, no puedes crear instancias de una clase abstracta hasta que todos los métodos abstractos estén implementados en una subclase.
Importa el módulo abc para crear clases abstractas:
from abc import ABC, abstractmethodCrea una clase abstracta con métodos abstractos:
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def describe(self):
return "This is a shape" # Método concreto (tiene implementación)El decorador @abstractmethod marca los métodos que las subclases deben implementar: estos son los «campos obligatorios» de tu formulario. Los métodos normales como describe() pueden tener implementaciones y se heredan tal cual: estos son los «campos rellenados previamente».
¿Por qué usar clases abstractas? Imponen un contrato. Si creas una clase Shape, estás diciendo: «toda forma DEBE poder calcular su área y su perímetro». Esto evita errores en los que podrías olvidar implementar métodos esenciales.
Intenta crear una instancia de la clase abstracta:
# Esto provocará un error:
# shape = Shape() # TypeError: Can't instantiate abstract classEste error es intencional: es como intentar enviar un formulario vacío. Python te está diciendo: «¡Todavía no has rellenado los campos obligatorios (los métodos area y perimeter)!»
Crea una subclase concreta que implemente todos los métodos abstractos:
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radiusAhora hemos «completado todos los campos obligatorios»: hemos implementado tanto area() como perimeter(). La clase Circle ahora es una clase concreta completa que se puede instanciar.
Ahora puedes crear instancias de la clase concreta:
circle = Circle(5)
print(circle.area())
print(circle.perimeter())
print(circle.describe()) # Método concreto heredadoCrea otra subclase concreta:
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width self.height
def perimeter(self):
return 2 * (self.width + self.height)
rectangle = Rectangle(4, 6)
print(rectangle.area())
print(rectangle.perimeter())Salida:
78.5
31.400000000000002
This is a shape
24
20Las clases abstractas definen un contrato que las subclases deben cumplir. Usa ABC y @abstractmethod para crear clases abstractas. Las subclases deben implementar todos los métodos abstractos o también serán abstractas. Esto garantiza la coherencia y evita implementaciones incompletas.
Desafío
IntermedioEn este desafío, implementarás un sistema de procesamiento de pagos.
Tienes que completar la implementación en estos archivos:
paymentmethod.py- Clase base abstracta con lógica de validacióncreditcard.py- Implementación de pagos con tarjeta de créditopaypal.py- Implementación de pagos con PayPal
Sigue los comentarios TODO de cada archivo para implementar la funcionalidad requerida. Los comentarios te guiarán paso a paso en la creación de métodos abstractos, la implementación de subclases concretas y el establecimiento de relaciones de importación adecuadas entre los archivos.
El archivo driver.py contiene casos de prueba que verificarán que tu implementación funcione correctamente, procesando distintos escenarios de pago y mostrando los detalles correspondientes para cada método de pago.
Pruébalo tú mismo
from creditcard import CreditCard
from paypal import PayPal
# Prueba las implementaciones - NO MODIFIQUES ESTE CÓDIGO DE PRUEBA
cc = CreditCard("1234567890123456")
pp = PayPal("user@example.com")
# Procesa pagos válidos
if cc.validate(100):
print(cc.process_payment(100))
print(cc.payment_details())
if pp.validate(200):
print(pp.process_payment(200))
print(pp.payment_details())
# Prueba con un importe no válido
if not pp.validate(0):
print("Invalid payment amount")
# Esto produciría un error si se quitara el comentario:
# pm = PaymentMethod() # No se puede instanciar una clase abstractaEsta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de la POO
Introducción a la POOArchivos externosClases vs. objetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Repaso: calculadora sencilla4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosRepaso: jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso: lista personalizada10Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador de propiedadDecorador de método estáticoDecorador de método de clase5Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas8Conceptos avanzados de POO
Composición frente a herenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs. variables de claseDecoradores de propiedadesAtributos privadosRepaso: gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online