- ¿Cuál es el problema?
- ¿Qué es el duck typing?
- Hacer nuestro código más limpio con duck typing
- ¿Qué es el polimorfismo?
- ¿Por qué evitar condiciones como isinstance?
- ¿Y si el método no existe?
A medida que aprendemos Python, nos encontramos con objetos de diferentes clases que comparten comportamientos similares. Por ejemplo, tanto Article como Topic permiten añadir y obtener comentarios, pero usan métodos distintos. ¿Cómo podemos hacer que una función trabaje con ambos sin complicarnos?
La solución es la duck typing (tipado pato): una técnica elegante que permite trabajar con objetos sin preocuparse por su tipo exacto. En esta lección, veremos cómo aplicarla en Python.
¿Cuál es el problema?
Pensemos en este caso: necesitamos una función has_comments() que diga si un objeto (ya sea un artículo o un tópico) tiene comentarios.
Una solución puede ser así:
def has_comments(commentable):
if isinstance(commentable, Article):
return len(commentable.get_article_comments()) > 0
elif isinstance(commentable, Topic):
return len(commentable.get_topic_comments()) > 0
Y los métodos usados en cada clase serían:
class Article:
def get_article_comments(self):
return self.comments
class Topic:
def get_topic_comments(self):
return self.comments
Pero este enfoque tiene un problema importante: si luego añadimos otro tipo de objeto (digamos, Post), debemos volver y modificar la función has_comments() otra vez. El código se vuelve más difícil de mantener y entender con el tiempo.
Aquí es cuando entra el duck typing, una forma de pensar típica en Python.
¿Qué es el duck typing?
Una expresión muy común en programación es:
Esto traduce a código como: si un objeto tiene el comportamiento que necesitamos (por ejemplo, un método get_comments()), entonces podemos usarlo sin importar su clase o tipo exacto.
En lugar de preguntar de qué clase es un objeto (usando isinstance), simplemente probamos si el objeto se comporta como esperamos. Lo único que nos interesa es que tenga el método y funcione.
Hacer nuestro código más limpio con duck typing
Rediseñemos nuestras clases para que compartan los mismos métodos clave: add_comment() y get_comments().
class Article:
def __init__(self):
self.comments = []
def add_comment(self, comment):
self.comments.append(comment)
def get_comments(self):
return self.comments
class Topic:
def __init__(self):
self.comments = []
def add_comment(self, comment):
self.comments.append(comment)
def get_comments(self):
return self.comments
Aquí ambas clases tienen un comportamiento común: permitir almacenar y obtener comentarios.
Ahora sí, podemos escribir una única función que funcione con ambas:
def has_comments(item):
# Aquí confiamos en que el objeto tiene un método llamado get_comments
return len(item.get_comments()) > 0
¿Vemos la diferencia? No preguntamos si el objeto es un Article o un Topic, simplemente asumimos que se comporta como cualquier cosa que tenga el método get_comments().
Probémoslo en acción
article = Article()
topic = Topic()
print(has_comments(article)) # False: no hay comentarios
article.add_comment("Buena explicación!")
print(has_comments(article)) # True: ya tiene un comentario
print(has_comments(topic)) # False
topic.add_comment("Buen tema!")
print(has_comments(topic)) # True
¡Y funciona perfectamente para ambos!
¿Qué es el polimorfismo?
Hasta ahora hemos aplicado algo que en programación se llama polimorfismo. Es una forma de que una misma función, como has_comments(), trabaje con diferentes tipos de objetos.
Hay dos tipos principales de polimorfismo:
| Tipo de Polimorfismo | ¿Qué hace? | Ejemplo |
|---|---|---|
| Polimorfismo de subtipos | Funciona con diferentes objetos que comparten una interfaz común (como get_comments()) |
Hemos usado este tipo con Article y Topic |
| Polimorfismo paramétrico | Usamos tipos genéricos que sirven para muchos tipos de datos (por ejemplo, listas de int, str) |
Usado con estructuras como list[T] |
En Python, no necesitamos herencia para lograr polimorfismo gracias a su tipo dinámico. Nos basta con que los objetos tengan el comportamiento esperado.
¿Por qué evitar condiciones como isinstance?
Porque hacen que el código sea:
❌ Más difícil de extender
❌ Más difícil de leer
❌ Más propenso a errores
En cambio, si usamos duck typing, logramos que nuestro código se enfoque en lo que hacen los objetos, no en lo que son.
¿Y si el método no existe?
Es cierto, puede fallar si el objeto no tiene ese método. Para manejarlo, podemos usar hasattr() o capturar una excepción. Pero lo más común es confiar en que los objetos que usamos sí implementan lo que prometen. Si todos siguen el mismo contrato (tener get_comments()), no hay problema.
Resumen
- Podemos escribir funciones que trabajen con diferentes tipos de objetos si ellos tienen los mismos métodos.
- No necesitamos usar
if isinstance(...); basta con asumir que un objeto se comporta como necesitamos. - El polimorfismo de subtipos nos permite tratar objetos distintos como si fueran iguales, siempre que compartan comportamiento.
- El polimorfismo paramétrico aplica cuando escribimos lógica genérica independiente del tipo de los elementos.
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.