Python
Tutorial Python: Polimorfismo
Aprende polimorfismo en Python con ejemplos de duck typing y métodos polimórficos para escribir código flexible y reutilizable.
Aprende Python y certifícateConcepto básico
El polimorfismo es uno de los pilares fundamentales de la programación orientada a objetos. La palabra proviene del griego "poly" (muchos) y "morphos" (formas), lo que refleja perfectamente su esencia: la capacidad de un objeto para tomar múltiples formas durante la ejecución de un programa.
En términos prácticos, el polimorfismo permite que objetos de diferentes clases respondan al mismo mensaje o método de manera distinta, cada uno según su propia implementación. Esta característica nos permite escribir código más flexible y reutilizable, ya que podemos tratar objetos de diferentes tipos de manera uniforme.
En Python, el polimorfismo se manifiesta de forma natural debido a su tipado dinámico. A diferencia de lenguajes más estrictos, Python no requiere que los objetos pertenezcan a una clase específica para poder invocar un método en ellos. Lo único que importa es que el objeto tenga implementado el método que estamos llamando.
Veamos un ejemplo sencillo para ilustrar este concepto:
def hacer_sonido(animal):
animal.hablar()
class Perro:
def hablar(self):
print("¡Guau!")
class Gato:
def hablar(self):
print("¡Miau!")
class Pato:
def hablar(self):
print("¡Cuac!")
# Creamos instancias de diferentes animales
fido = Perro()
felix = Gato()
donald = Pato()
# La misma función funciona con diferentes tipos de objetos
hacer_sonido(fido) # Imprime: ¡Guau!
hacer_sonido(felix) # Imprime: ¡Miau!
hacer_sonido(donald) # Imprime: ¡Cuac!
En este ejemplo, la función hacer_sonido()
trabaja con cualquier objeto que tenga un método hablar()
, sin importar a qué clase pertenezca. Cada animal "habla" a su manera, pero todos responden al mismo mensaje. Esto es la esencia del polimorfismo.
Tipos de polimorfismo en Python
En Python podemos identificar principalmente dos tipos de polimorfismo:
Polimorfismo de sobrecarga de métodos: Ocurre cuando una clase hija redefine un método de su clase padre. En Python, esto se conoce como sobreescritura de métodos.
Polimorfismo de interfaz: Cuando diferentes clases implementan los mismos métodos, permitiendo que sean tratadas de manera uniforme. En Python, esto se implementa a través del concepto de "duck typing" (que veremos en la siguiente sección).
Veamos un ejemplo del primer tipo, la sobreescritura de métodos:
class Animal:
def desplazarse(self):
print("El animal se desplaza")
class Pez(Animal):
def desplazarse(self):
print("El pez nada")
class Ave(Animal):
def desplazarse(self):
print("El ave vuela")
class Serpiente(Animal):
def desplazarse(self):
print("La serpiente repta")
# Creamos instancias de diferentes animales
animal_generico = Animal()
nemo = Pez()
piolin = Ave()
kaa = Serpiente()
# Cada animal se desplaza a su manera
animal_generico.desplazarse() # Imprime: El animal se desplaza
nemo.desplazarse() # Imprime: El pez nada
piolin.desplazarse() # Imprime: El ave vuela
kaa.desplazarse() # Imprime: La serpiente repta
En este ejemplo, cada clase hereda de Animal
y sobreescribe el método desplazarse()
para implementar su propio comportamiento. Cuando llamamos al método, Python ejecuta la versión correspondiente a la clase del objeto.
Ventajas del polimorfismo
El polimorfismo ofrece varias ventajas importantes:
- Flexibilidad: Permite tratar objetos de diferentes clases de manera uniforme.
- Extensibilidad: Facilita añadir nuevas clases sin modificar el código existente.
- Reutilización de código: Permite escribir funciones que trabajen con múltiples tipos de objetos.
- Abstracción: Nos permite centrarnos en lo que hacen los objetos, no en cómo lo hacen.
Polimorfismo con funciones y métodos integrados
Python también aplica el polimorfismo en sus funciones y métodos integrados. Por ejemplo, el operador +
funciona de manera diferente según el tipo de datos:
# Con números realiza suma aritmética
print(5 + 3) # Imprime: 8
# Con cadenas realiza concatenación
print("Hola " + "mundo") # Imprime: Hola mundo
# Con listas realiza unión
print([1, 2] + [3, 4]) # Imprime: [1, 2, 3, 4]
De manera similar, la función len()
funciona con diferentes tipos de colecciones:
# Longitud de una cadena
print(len("Python")) # Imprime: 6
# Número de elementos en una lista
print(len([1, 2, 3, 4])) # Imprime: 4
# Número de pares clave-valor en un diccionario
print(len({"a": 1, "b": 2})) # Imprime: 2
Estos son ejemplos de polimorfismo paramétrico, donde una misma operación se comporta de manera diferente según el tipo de los argumentos.
Implementación del polimorfismo en clases propias
Podemos implementar comportamiento polimórfico en nuestras propias clases definiendo métodos con el mismo nombre. Por ejemplo, podemos crear diferentes formas geométricas que calculen su área de manera distinta:
class Forma:
def area(self):
pass # Método base que será sobreescrito
class Rectangulo(Forma):
def __init__(self, ancho, alto):
self.ancho = ancho
self.alto = alto
def area(self):
return self.ancho * self.alto
class Circulo(Forma):
def __init__(self, radio):
self.radio = radio
def area(self):
return 3.14159 * self.radio ** 2
# Función que trabaja con cualquier forma
def imprimir_area(forma):
print(f"El área es: {forma.area()}")
# Creamos diferentes formas
rectangulo = Rectangulo(5, 4)
circulo = Circulo(3)
# La misma función funciona con diferentes formas
imprimir_area(rectangulo) # Imprime: El área es: 20
imprimir_area(circulo) # Imprime: El área es: 28.27431
En este ejemplo, la función imprimir_area()
trabaja con cualquier objeto que tenga un método area()
, sin importar cómo esté implementado internamente. Cada forma calcula su área según sus propias características, pero todas responden al mismo mensaje.
El polimorfismo es una herramienta poderosa que nos permite escribir código más modular, flexible y mantenible. En las siguientes secciones, exploraremos conceptos relacionados como el duck typing y los métodos polimórficos en mayor profundidad.
Duck typing
El duck typing es un concepto fundamental en Python que está estrechamente relacionado con el polimorfismo. Su nombre proviene de la expresión en inglés "If it walks like a duck and quacks like a duck, then it probably is a duck" (Si camina como un pato y grazna como un pato, probablemente sea un pato).
En Python, el duck typing se refiere a la forma en que el lenguaje determina si un objeto puede ser utilizado para un propósito particular. A diferencia de los lenguajes con tipado estático, Python no se preocupa por el tipo o la clase de un objeto, sino por su comportamiento - específicamente, qué métodos y atributos tiene disponibles.
Cómo funciona el duck typing
En lugar de verificar el tipo de un objeto, Python intenta usar el objeto como si tuviera los métodos y atributos necesarios. Si los tiene, el código funciona; si no, se produce un error en tiempo de ejecución.
Veamos un ejemplo sencillo:
def calcular_precio_total(productos):
total = 0
for producto in productos:
total += producto.precio
return total
Esta función no especifica qué tipo de objetos debe contener la lista productos
. Lo único que importa es que cada objeto tenga un atributo precio
. Podrían ser instancias de diferentes clases:
class Libro:
def __init__(self, titulo, precio):
self.titulo = titulo
self.precio = precio
class Electronico:
def __init__(self, nombre, precio, garantia):
self.nombre = nombre
self.precio = precio
self.garantia = garantia
# Creamos una lista mixta de productos
carrito = [
Libro("Python Cookbook", 39.99),
Electronico("Teclado", 59.99, "2 años"),
Libro("Fluent Python", 49.99)
]
# La función funciona con cualquier objeto que tenga un atributo 'precio'
print(f"Total: ${calcular_precio_total(carrito):.2f}") # Imprime: Total: $149.97
La función calcular_precio_total()
trabaja con cualquier colección de objetos que tengan un atributo precio
, sin importar a qué clase pertenezcan o qué otros atributos tengan.
Duck typing vs. herencia
Una ventaja importante del duck typing es que no requiere relaciones de herencia entre clases. Dos clases pueden ser completamente independientes, pero si implementan los mismos métodos, pueden ser tratadas de manera uniforme.
Comparemos el enfoque de duck typing con el enfoque tradicional basado en herencia:
# Enfoque con herencia
class Reproductor:
def reproducir(self):
raise NotImplementedError("Las subclases deben implementar este método")
class ReproductorMP3(Reproductor):
def reproducir(self):
print("Reproduciendo archivo MP3...")
class ReproductorWAV(Reproductor):
def reproducir(self):
print("Reproduciendo archivo WAV...")
# Enfoque con duck typing
class ArchivoMP3:
def reproducir(self):
print("Reproduciendo archivo MP3...")
class ArchivoWAV:
def reproducir(self):
print("Reproduciendo archivo WAV...")
class ArchivoOGG:
def reproducir(self):
print("Reproduciendo archivo OGG...")
Con el enfoque de herencia, todas las clases deben heredar de Reproductor
. Con duck typing, las clases pueden ser completamente independientes, siempre que implementen el método reproducir()
.
La función que utiliza estos objetos sería la misma en ambos casos:
def reproducir_audio(archivo):
archivo.reproducir()
# Funciona con cualquier objeto que tenga un método 'reproducir'
mp3 = ArchivoMP3()
wav = ArchivoWAV()
ogg = ArchivoOGG()
reproducir_audio(mp3) # Imprime: Reproduciendo archivo MP3...
reproducir_audio(wav) # Imprime: Reproduciendo archivo WAV...
reproducir_audio(ogg) # Imprime: Reproduciendo archivo OGG...
Ventajas del duck typing
El duck typing ofrece varias ventajas significativas:
- Flexibilidad: Permite crear código que trabaja con tipos de objetos que ni siquiera existían cuando se escribió el código.
- Desacoplamiento: Reduce la dependencia entre diferentes partes del código.
- Simplicidad: Elimina la necesidad de jerarquías de clases complejas.
- Extensibilidad: Facilita añadir nuevos tipos sin modificar el código existente.
Duck typing en la biblioteca estándar
La biblioteca estándar de Python hace un uso extensivo del duck typing. Por ejemplo, muchas funciones que trabajan con secuencias no requieren que el objeto sea específicamente una lista o una tupla, solo que se comporte como una secuencia:
# La función sum() funciona con cualquier iterable que contenga números
print(sum([1, 2, 3])) # Lista: 6
print(sum((1, 2, 3))) # Tupla: 6
print(sum({1, 2, 3})) # Conjunto: 6
print(sum(range(1, 4))) # Objeto range: 6
# La función sorted() funciona con cualquier iterable
print(sorted("python")) # Cadena: ['h', 'n', 'o', 'p', 't', 'y']
print(sorted({'a': 1, 'c': 3, 'b': 2})) # Diccionario (ordena las claves): ['a', 'b', 'c']
Protocolos en Python
En Python, los protocolos son conjuntos de métodos que un objeto debe implementar para comportarse de cierta manera. Son como interfaces informales. Algunos ejemplos comunes:
- Protocolo de iteración: Objetos que implementan
__iter__()
y__next__()
pueden usarse en buclesfor
. - Protocolo de contexto: Objetos que implementan
__enter__()
y__exit__()
pueden usarse con la declaraciónwith
. - Protocolo de secuencia: Objetos que implementan
__len__()
y__getitem__()
pueden usarse como secuencias.
Veamos un ejemplo de una clase que implementa el protocolo de iteración:
class Contador:
def __init__(self, inicio, fin):
self.inicio = inicio
self.fin = fin
self.valor = inicio
def __iter__(self):
self.valor = self.inicio
return self
def __next__(self):
if self.valor > self.fin:
raise StopIteration
valor_actual = self.valor
self.valor += 1
return valor_actual
# Podemos usar nuestra clase en un bucle for
for num in Contador(1, 5):
print(num) # Imprime: 1, 2, 3, 4, 5
Aunque Contador
no hereda de ninguna clase específica, puede usarse en un bucle for
porque implementa el protocolo de iteración.
Verificación de tipos en duck typing
Si bien el duck typing se basa en la idea de "intentar y ver qué pasa", a veces es útil verificar si un objeto tiene ciertos métodos antes de usarlos. Python proporciona la función hasattr()
para este propósito:
def procesar_archivo(archivo):
if hasattr(archivo, 'leer') and callable(archivo.leer):
contenido = archivo.leer()
# Procesar el contenido
else:
raise TypeError("El objeto no tiene un método 'leer'")
Sin embargo, en la práctica, muchos programadores de Python prefieren el enfoque EAFP (Easier to Ask for Forgiveness than Permission - Es más fácil pedir perdón que permiso):
def procesar_archivo(archivo):
try:
contenido = archivo.leer()
# Procesar el contenido
except AttributeError:
raise TypeError("El objeto no tiene un método 'leer'")
Este enfoque es más idiomático en Python y evita la verificación redundante de atributos.
Duck typing y anotaciones de tipo
Con la introducción de las anotaciones de tipo en Python 3.5+, surgió la pregunta de cómo conciliar el duck typing con la verificación estática de tipos. La respuesta vino en forma de protocolos estructurales en el módulo typing
:
from typing import Protocol, List
class Producto(Protocol):
precio: float
def calcular_precio_total(productos: List[Producto]) -> float:
total = 0
for producto in productos:
total += producto.precio
return total
Esto permite mantener la flexibilidad del duck typing mientras se proporciona información para herramientas de verificación estática de tipos como mypy.
El duck typing es una característica fundamental de Python que promueve un estilo de programación flexible y pragmático. En lugar de preocuparse por las jerarquías de clases, nos permite centrarnos en lo que los objetos pueden hacer, lo que resulta en código más simple y adaptable.
Métodos polimórficos
Los métodos polimórficos son el mecanismo práctico mediante el cual implementamos el polimorfismo en Python. Estos métodos comparten el mismo nombre en diferentes clases pero tienen implementaciones específicas adaptadas a cada clase. Cuando invocamos un método polimórfico, Python ejecuta la versión correspondiente al tipo de objeto con el que estamos trabajando.
En Python, los métodos polimórficos se pueden implementar de varias formas, cada una con sus propias características y casos de uso. Veamos las principales técnicas para crear y utilizar métodos polimórficos.
Sobreescritura de métodos
La forma más común de crear métodos polimórficos es mediante la sobreescritura (override) de métodos en clases derivadas. Cuando una clase hereda de otra, puede redefinir los métodos de la clase base para proporcionar una implementación específica:
class Instrumento:
def tocar(self):
print("Tocando un instrumento genérico")
def afinar(self):
print("Afinando instrumento")
class Guitarra(Instrumento):
def tocar(self):
print("Rasgueando las cuerdas de la guitarra")
class Piano(Instrumento):
def tocar(self):
print("Presionando teclas del piano")
class Bateria(Instrumento):
def tocar(self):
print("Golpeando los tambores y platillos")
Ahora podemos crear una función que trabaje con cualquier tipo de instrumento:
def concierto(instrumento):
instrumento.afinar()
instrumento.tocar()
# Creamos diferentes instrumentos
guitarra = Guitarra()
piano = Piano()
bateria = Bateria()
# La misma función funciona con diferentes instrumentos
concierto(guitarra)
# Salida:
# Afinando instrumento
# Rasgueando las cuerdas de la guitarra
concierto(piano)
# Salida:
# Afinando instrumento
# Presionando teclas del piano
Observa que el método afinar()
no fue sobreescrito, por lo que se utiliza la implementación de la clase base. Sin embargo, cada instrumento implementa su propia versión del método tocar()
.
Métodos abstractos
Para garantizar que las clases derivadas implementen ciertos métodos, podemos utilizar métodos abstractos mediante el módulo abc
(Abstract Base Classes):
from abc import ABC, abstractmethod
class FiguraGeometrica(ABC):
@abstractmethod
def calcular_area(self):
pass
@abstractmethod
def calcular_perimetro(self):
pass
def describir(self):
return f"Área: {self.calcular_area()}, Perímetro: {self.calcular_perimetro()}"
class Rectangulo(FiguraGeometrica):
def __init__(self, base, altura):
self.base = base
self.altura = altura
def calcular_area(self):
return self.base * self.altura
def calcular_perimetro(self):
return 2 * (self.base + self.altura)
class Circulo(FiguraGeometrica):
def __init__(self, radio):
self.radio = radio
def calcular_area(self):
return 3.14159 * self.radio ** 2
def calcular_perimetro(self):
return 2 * 3.14159 * self.radio
Los métodos abstractos definen una interfaz que todas las clases derivadas deben implementar. Si intentamos crear una instancia de una clase que no implementa todos los métodos abstractos, Python lanzará un error:
# Esto funcionará correctamente
rectangulo = Rectangulo(5, 3)
print(rectangulo.describir()) # Área: 15, Perímetro: 16
circulo = Circulo(4)
print(circulo.describir()) # Área: 50.26544, Perímetro: 25.13272
# Esto causaría un error
class TrianguloIncompleto(FiguraGeometrica):
def calcular_area(self):
return 10
# Falta implementar calcular_perimetro()
# TypeError: Can't instantiate abstract class TrianguloIncompleto with abstract method calcular_perimetro
Métodos mágicos (dunder methods)
Los métodos mágicos (también llamados métodos dunder por su doble guion bajo o "double underscore") son una forma poderosa de implementar polimorfismo en Python. Estos métodos permiten que nuestras clases respondan a operadores y funciones integradas:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, otro):
return Vector2D(self.x + otro.x, self.y + otro.y)
def __mul__(self, escalar):
return Vector2D(self.x * escalar, self.y * escalar)
def __str__(self):
return f"({self.x}, {self.y})"
# Creamos vectores
v1 = Vector2D(3, 4)
v2 = Vector2D(1, 2)
# Usamos operadores con nuestros objetos
v3 = v1 + v2
print(v3) # (4, 6)
v4 = v1 * 2
print(v4) # (6, 8)
Los métodos mágicos nos permiten definir cómo nuestros objetos deben comportarse con operadores y funciones integradas de Python. Algunos métodos mágicos comunes incluyen:
__str__
y__repr__
: Para representación como cadena__len__
: Para obtener la longitud conlen()
__getitem__
y__setitem__
: Para acceso con notación de índiceobj[key]
__eq__
,__lt__
, etc.: Para comparaciones__call__
: Para hacer que el objeto sea llamable como una función
Métodos de clase y métodos estáticos
Los métodos de clase y métodos estáticos también pueden ser polimórficos:
class Conversor:
@staticmethod
def convertir(valor):
return valor # Implementación base
class ConversorTemperatura(Conversor):
@staticmethod
def convertir(celsius):
# Convierte de Celsius a Fahrenheit
return (celsius * 9/5) + 32
class ConversorLongitud(Conversor):
@staticmethod
def convertir(metros):
# Convierte de metros a pies
return metros * 3.28084
# Uso polimórfico
print(ConversorTemperatura.convertir(25)) # 77.0
print(ConversorLongitud.convertir(10)) # 32.8084
Implementación de múltiples interfaces
A diferencia de otros lenguajes, Python no tiene un concepto formal de interfaces. Sin embargo, gracias al duck typing, podemos implementar múltiples "interfaces" simplemente proporcionando los métodos necesarios:
class ArchivoTexto:
def __init__(self, contenido):
self.contenido = contenido
def leer(self):
return self.contenido
def escribir(self, texto):
self.contenido = texto
# Implementa protocolo de contexto
def __enter__(self):
print("Abriendo archivo")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Cerrando archivo")
# Implementa protocolo de iteración
def __iter__(self):
self.indice = 0
self.lineas = self.contenido.split('\n')
return self
def __next__(self):
if self.indice >= len(self.lineas):
raise StopIteration
linea = self.lineas[self.indice]
self.indice += 1
return linea
Esta clase implementa múltiples protocolos:
# Como objeto de lectura/escritura
archivo = ArchivoTexto("Línea 1\nLínea 2\nLínea 3")
print(archivo.leer())
archivo.escribir("Nuevo contenido")
# Como administrador de contexto (with)
with ArchivoTexto("Hola mundo") as f:
print(f.leer())
# Salida:
# Abriendo archivo
# Hola mundo
# Cerrando archivo
# Como iterable
archivo = ArchivoTexto("Primera línea\nSegunda línea\nTercera línea")
for linea in archivo:
print(f"- {linea}")
# Salida:
# - Primera línea
# - Segunda línea
# - Tercera línea
Métodos polimórficos con parámetros variables
Los métodos polimórficos pueden tener diferentes parámetros en diferentes clases, lo que permite una mayor flexibilidad:
class Notificador:
def enviar(self, mensaje):
print(f"Enviando: {mensaje}")
class NotificadorEmail(Notificador):
def enviar(self, mensaje, asunto="Sin asunto", destinatario="usuario@ejemplo.com"):
print(f"Enviando email a {destinatario}")
print(f"Asunto: {asunto}")
print(f"Mensaje: {mensaje}")
class NotificadorSMS(Notificador):
def enviar(self, mensaje, numero="123456789"):
print(f"Enviando SMS al {numero}: {mensaje}")
Sin embargo, al usar estos objetos de manera polimórfica, debemos tener cuidado de proporcionar solo los parámetros que todas las implementaciones aceptan:
def notificar_usuario(notificador, mensaje):
# Solo usamos el parámetro común a todas las implementaciones
notificador.enviar(mensaje)
notificar_usuario(Notificador(), "Alerta general")
notificar_usuario(NotificadorEmail(), "Tu cuenta ha sido verificada")
notificar_usuario(NotificadorSMS(), "Código de verificación: 1234")
Patrones comunes con métodos polimórficos
Existen varios patrones de diseño que aprovechan los métodos polimórficos:
- Patrón Estrategia: Define una familia de algoritmos intercambiables:
class EstrategiaOrdenamiento:
def ordenar(self, datos):
pass
class OrdenamientoBurbuja(EstrategiaOrdenamiento):
def ordenar(self, datos):
print("Ordenando con método burbuja")
# Implementación del algoritmo burbuja
return sorted(datos) # Simplificado para el ejemplo
class OrdenamientoRapido(EstrategiaOrdenamiento):
def ordenar(self, datos):
print("Ordenando con método rápido")
# Implementación del algoritmo quicksort
return sorted(datos) # Simplificado para el ejemplo
class Ordenador:
def __init__(self, estrategia=None):
self.estrategia = estrategia or OrdenamientoBurbuja()
def ordenar(self, datos):
return self.estrategia.ordenar(datos)
def cambiar_estrategia(self, estrategia):
self.estrategia = estrategia
# Uso
ordenador = Ordenador()
print(ordenador.ordenar([3, 1, 4, 1, 5, 9, 2]))
# Cambiamos la estrategia
ordenador.cambiar_estrategia(OrdenamientoRapido())
print(ordenador.ordenar([3, 1, 4, 1, 5, 9, 2]))
- Patrón Comando: Encapsula una solicitud como un objeto:
class Comando:
def ejecutar(self):
pass
def deshacer(self):
pass
class ComandoInsertar(Comando):
def __init__(self, documento, texto):
self.documento = documento
self.texto = texto
self.posicion = len(documento.contenido)
def ejecutar(self):
self.documento.contenido += self.texto
def deshacer(self):
self.documento.contenido = self.documento.contenido[:-len(self.texto)]
class ComandoBorrar(Comando):
def __init__(self, documento, cantidad):
self.documento = documento
self.cantidad = cantidad
self.texto_borrado = ""
def ejecutar(self):
if len(self.documento.contenido) >= self.cantidad:
self.texto_borrado = self.documento.contenido[-self.cantidad:]
self.documento.contenido = self.documento.contenido[:-self.cantidad]
def deshacer(self):
self.documento.contenido += self.texto_borrado
class Documento:
def __init__(self):
self.contenido = ""
self.historial = []
def ejecutar_comando(self, comando):
comando.ejecutar()
self.historial.append(comando)
def deshacer(self):
if self.historial:
comando = self.historial.pop()
comando.deshacer()
Consideraciones de rendimiento
Los métodos polimórficos en Python son muy flexibles, pero pueden tener implicaciones de rendimiento:
- La resolución dinámica de métodos (buscar el método correcto en tiempo de ejecución) puede ser más lenta que las llamadas directas.
- El duck typing puede ocultar errores hasta el tiempo de ejecución.
Para casos donde el rendimiento es crítico, podemos considerar:
# Verificación explícita de tipo (cuando sea necesario)
def procesar_figura(figura):
if not isinstance(figura, FiguraGeometrica):
raise TypeError("Se esperaba una FiguraGeometrica")
return figura.calcular_area()
# Caching de métodos para evitar búsquedas repetidas
def optimizar_llamadas(objeto, metodo_nombre):
metodo = getattr(objeto, metodo_nombre)
def wrapper(*args, **kwargs):
return metodo(*args, **kwargs)
return wrapper
Sin embargo, en la mayoría de los casos, la claridad y flexibilidad del código son más importantes que estas optimizaciones prematuras.
Los métodos polimórficos son una herramienta fundamental en Python que nos permite escribir código más flexible, extensible y mantenible. Al comprender cómo implementarlos y utilizarlos efectivamente, podemos aprovechar todo el poder del polimorfismo en nuestras aplicaciones.
Otros ejercicios de programación de Python
Evalúa tus conocimientos de esta lección Polimorfismo con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el concepto de polimorfismo y su importancia en la programación orientada a objetos.
- Identificar los tipos de polimorfismo en Python, incluyendo sobreescritura de métodos y polimorfismo de interfaz.
- Entender el duck typing como mecanismo de polimorfismo basado en el comportamiento de los objetos.
- Aprender a implementar métodos polimórficos mediante sobreescritura, métodos abstractos y métodos mágicos.
- Reconocer las ventajas y aplicaciones prácticas del polimorfismo y duck typing en el desarrollo de código modular y extensible.