¿Qué es la abstracción?


Este concepto se usa para esconder los detalles complicados y que no son importantes para los usuarios.
Un ejemplo de abstracción en el mundo real, se da en este mismo sitio web.
Tú lo estás leyendo, pero no tienes porqué saber, de qué forma lo he creado o como funcionan los lenguajes de programación web para presentarlo. Solo tienes que leerlo e interactuar con él.
Abstracción en inglés es abstraction.
Espacio publicitario
Otro ejemplo de abstracción, se da con un vehículo. Para conducirlo, necesitas saber una serie de “funciones”, como girar, acelerar, cambiar marcha, frenar, etc.
Sin embargo, para conducir no tienes porqué saber como ocurren todas esas cosas, ni hasta la última pieza implicada en ello.
Este concepto de abstracción, se puede llevar a las clases de Python.
Abstracción vs. Encapsulamiento
No confundas lo que vimos en el capítulo de encapsulamiento, con la abstracción.
Encapsulamiento
Se centra en cómo se implementa una clase u objeto, ocultando sus detalles internos y protegiendo sus datos.
Es el proceso de agrupar atributos y métodos relacionados dentro de un conjunto, protegiéndolos del acceso externo.
En Python, más que protegerlos del acceso externo, podríamos decir con mayor precisión, que define qué miembros deberían ser accesibles desde el exterior.
Recuerda que en Python, todo esto se implementa con convenciones, no se trata de una “protección real” como la que implementan lenguajes de programación como Java.
Abstracción
Se centra en qué hace una clase u objeto, sin revelar cómo lo hace.
La abstracción permite crear elementos simples y fáciles de usar, eliminando detalles innecesarios y centrándose en las características esenciales para los usuarios.
Por ejemplo, gracias a la abstracción, puedes utilizar las listas (objetos de la clase list), sin ni siquiera saber como realizan las acciones internamente sus métodos; solo los llamas con una serie de argumentos, y estos hacen todo el trabajo internamente.
En otras palabras, el encapsulamiento se preocupa por la estructura interna de una clase u objeto, mientras que la abstracción se preocupa por su comportamiento externo.
Espacio publicitario
Clases abstractas
Las clases abstractas son clases diseñadas para ser utilizadas solo por subclases, mediante la herencia.
Cuando un método es declarado dentro de la clase, sin su implementación, se conoce como método abstracto.
Una clase abstracta no puede ser instanciada. Es decir, no puedes crear objetos de esa clase, pero sí de sus subclases.
Las clases abstractas sirven para definir una estructura general para las subclases.
Todas las subclases que hereden de la clase abstracta, deberán implementar los métodos de la clase abstracta, con sus propias características.
A continuación, tienes un ejemplo, que seguro, te ilustrará más que todas estas explicaciones teóricas:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def hablar(self):
pass
class Perro(Animal):
def hablar(self):
print("Woof!")
class Gato(Animal):
def hablar(self):
print("Meow!")
perro = Perro()
perro.hablar()
gato = Gato()
gato.hablar()
Woof!
Meow!
La clase abstracta es la que está marcada en negrita
(Animal
).
Si estas clases pertenecieran a un módulo o biblioteca
completa, para utilizar los métodos que puedan tener la
clase Perro
o Gato
, no
necesitarías saber que existe una clase
Animal
.
Para hacer uso de las clases abstractas, tendrás que
importar el módulo abc
(línea 1 en el ejemplo
de código anterior).
Si no entiendes todavía sobre temas de importaciones, no pasa nada, es un tema que empiezo a explicar en los próximos capítulos.
Simplemente, debes poner esa línea tal y como está.
En los paréntesis de la clase abstracta, heredamos de la
clase ABC
, creando así una clase abstracta.
Después utilizamos un elemento conocido como decorador,
llamado @abstractmethod
. Este decorador se
encarga de crear los métodos abstractos dentro de la clase
abstracta.
También hablo en este curso sobre los decoradores; unos capítulos más adelante, en la parte de programación modular.
Entonces hablar()
, es un método abstracto.
Espacio publicitario
Si te fijas, las dos subclases, implementan su propio
método hablar()
.
Al tratarse de una herencia de clase abstracta, estamos obligados a definir en las subclases los métodos abstractos.
Si no lo hacemos, mira lo que ocurre:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def hablar(self):
pass
class Perro(Animal):
def correr(self):
print("Ha empezado a correr.")
perro = Perro()
perro.correr()
TypeError: Can't instantiate abstract class Perro without an implementation for abstract method 'hablar'
Error de tipo: No se puede instanciar la clase abstracta Perro sin la implementación para el método abstracto 'hablar'
El error nos dice, que la clase
Perro
, no tiene el método abstractohablar()
, y que sin él, no se puede instanciar el objeto.
También, si intentas instanciar de una clase abstracta directamente, recibirás el mismo error:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def hablar(self):
pass
abstracta = Animal()
TypeError: Can't instantiate abstract class Animal without an implementation for abstract method 'hablar'
Error de tipo: No se puede instanciar la clase abstracta Animal sin la implementación para el método abstracto 'hablar'
Description
Ventajas principales de la abstracción
La abstracción hace el código más simple, favorece su reutilización, mejora la escalabilidad, el trabajo en equipo, la flexibilidad y la modularidad. Permite centrarse en la lógica del problema, en lugar de la complejidad interna.
La abstracción permite al programador centrarse en la lógica del problema que se está resolviendo (qué se quiere hacer), sin tener que preocuparse por los detalles de la implementación (cómo se hace). Esto nos lleva a escribir un código más óptimo, ya que el programador no se distrae o pierde tiempo con detalles innecesarios.
Espacio publicitario
Espacio publicitario
Ejercicios de Python para resolver
35. Crea una clase abstracta llamada
Producto
.
Creamos la clase abstracta heredando de
ABC
:
from abc import ABC, abstractmethod
class Producto(ABC):
pass
36. Añade a la clase Producto
tres métodos
abstractos:
obtener_nombre()
obtener_precio()
obtener_peso()
Creamos los tres métodos abstractos. Estos todavía no hacen nada:
from abc import ABC, abstractmethod
class Producto(ABC):
@abstractmethod
def obtener_nombre(self):
pass
@abstractmethod
def obtener_precio(self):
pass
# Peso en kilogramos o litros
@abstractmethod
def obtener_peso(self):
pass
37. Crea dos subclases que hereden de la clase abstracta:
Alimento
Bebida
Creamos las dos clases vacías:
class Alimento(Producto):
pass
class Bebida(Producto):
pass
38. En el __init__
de las dos clases
anteriores, añade los siguientes atributos:
nombre
precio
peso
Añadimos el método __init__
en cada
clase, con los atributos solicitados:
class Alimento(Producto):
def __init__(self, nombre, precio, peso):
self.nombre = nombre
self.precio = precio
self.peso = peso
class Bebida(Producto):
def __init__(self, nombre, precio, peso):
self.nombre = nombre
self.precio = precio
self.peso = peso
39. Implementa los tres métodos abstractos en cada clase. Estos tan solo devolverán los valores de cada atributo.
Añadimos la implementación de los tres métodos en cada clase:
class Alimento(Producto):
def __init__(self, nombre, precio, peso):
self.nombre = nombre
self.precio = precio
self.peso = peso
def obtener_nombre(self):
return self.nombre
def obtener_precio(self):
return self.precio
def obtener_peso(self):
return self.peso
class Bebida(Producto):
def __init__(self, nombre, precio, peso):
self.nombre = nombre
self.precio = precio
self.peso = peso
def obtener_nombre(self):
return self.nombre
def obtener_precio(self):
return self.precio
def obtener_peso(self):
return self.peso
40. Crea dos objetos. Uno de la clase
Alimento
, y otro de la clase
Bebida
. Por ejemplo, estos objetos pueden ser
galletas y agua, o cualquier otra cosa que encaje con cada
clase.
Creamos los dos objetos:
galletas = Alimento("Neuronal biscuits", 1.55, 0.2)
agua = Bebida("Agua espacial refinada", 0.85, 1.5)
41. Utiliza con cada objeto, cada método. Así comprobarás que se ha implementado correctamente la clase abstracta.
A partir de definir la estructura base, implementando todos los métodos abstractos, puedes incluir métodos propios a cada subclase. No tienen por qué ser clones unas de otras, si no, no tendría sentido tener más de una clase, para hacer exactamente lo mismo.
Probamos los tres métodos, con los dos objetos:
galletas = Alimento("Neuronal biscuits", 1.55, 0.2)
print(galletas.obtener_nombre())
print(galletas.obtener_precio())
print(galletas.obtener_peso())
agua = Bebida("Agua espacial refinada", 0.85, 1.5)
print(agua.obtener_nombre())
print(agua.obtener_precio())
print(agua.obtener_peso())
Neuronal biscuits
1.55
0.2
Agua espacial refinada
0.85
1.5
Espacio publicitario