No dejes que el título de este artículo te engañe.
Puede parecer un ejercicio simple, casi para principiantes.
Y puede serlo, pero también puede ser una masterclass alucinante sobre Python. Y eso es lo que pretendo con este artículo.
Sigue leyendo y lo comprobarás.
Se trataba de hacer una función de Python que reciba una cadena de texto y devuelva otra, que es la de entrada pero sin las vocales.
Por ejemplo, para la entrada 'Bob Esponja'
, debe devolver 'Bb spnj
'. (¿Quién vive en la piña debajo del mar?)
- Solución tradicional
- Solución tradicional mejorada
- Eliminando vocales usando comprensiones de listas
- Mejoras varias
- Con programación funcional y filter
- Con expresiones regulares
- Diccionarios de traducción para cadenas de texto: maketrans y translate
- Con programación funcional y reduce
- Mejoremos la solución tradicional: deque
- Conclusiones
Pues vamos allá.
Solución tradicional
Primero, una solución “tradicional” que funciona y es relativamente fácil de leer y de entender.
def eliminar_vocales(cadena):
resultado = ''
vocales = 'aeiouAEIOU'
for c in cadena:
if c not in "aeiou":
resultado += c
return resultado
Simplemente vamos recorriendo la cadena. Si encontramos una consonante la añadimos al resultado final.
Fíjate que para comprobar si una letra es una vocal usamos el operador in
con una cadena que contiene las vocales en minúsculas o en mayúsculas 'aeiouAEIUO'
. Esto devolverá False
(por usar not in
) si la letra es una vocal.
¿Por qué no me gusta esta solución?
Precisamente por ser demasiado tradicional. Python es un lenguaje moderno y de alto nivel y, normalmente, podemos huir de este tipo de soluciones.
Además, en cada vuelta del bucle, y cada vez que encontramos una consonante, se crea una nueva cadena de texto al usar el operador +
. Hay que recordar que las cadenas de texto en Python son no mutables, es decir, no podemos modificarlas, de forma que hay que crear una nueva cada vez que queramos cambiar una.
Veamos otra solución similar pero un poco más pitónica.
Solución tradicional mejorada
def eliminar_vocales(cadena):
vocales = 'aeiouAEIOU'
for vocal in vocales:
cadena = cadena.replace(vocal, '')
return cadena
Es un poco mejor porque hacemos uso de una de las funciones de las cadenas, que es replace
, para eliminar caracteres. Aquí, lo que hago es sustituir cada vocal por la cadena vacía ''
, de forma que el resultado es una eliminación.
Pero tampoco me gusta y es poco eficiente por dos motivos.
Te envío todos los días un consejo para que cada día seas mejor en Python.
Siempre sobre Python y programación.
Más de 2500 personas como tú los reciben cada día.
Día que estás fuera, consejo sobre Python que te pierdes.
Antes de suscribirte consulta aquí la
Finalidad de recogida y tratamiento de datos personales: enviarte boletín informativo de Python y comunicaciones comerciales.
Legitimación: tu consentimiento.
Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (GetResponse).
Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control.
Más información: política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal.
En el bucle, recorremos la cadena una y otra vez (que podría ser muy larga) para eliminar la vocales (esto lo hace replace
). Y no solo eso, si no que se crea una nueva cadena tras la eliminación de cada vocal. También podemos (y debemos) huir de eso.
Vale, movámonos hacia una solución más pitonesca, más pitónica, más pitoniana…
Eliminando vocales usando comprensiones de listas
Usemos comprensiones… y join
.
def eliminar_vocales(cadena):
vocales = 'aeiouAEIOU'
return ''.join([c for c in cadena if c not in vocales])
Mejor, ¿no?
Esto me va gustando más. Creo que a ti también.
Creamos una lista a partir de las letras que no son vocales y finalmente convertimos la lista a una cadena con join
(que une distintas cadenas de texto usando otra cadena como conector, en este caso, la cadena vacía).
Pero no acaba aquí la cosa, sigamos viendo opciones y mejoras.
Mejoras varias
Hay algo en esa última solución que me chirría. Y es considerar de esa manera las vocales minúsculas y las mayúsculas.
Trabajemos solo con las minúsculas y hagamos una transformación a cada letra de la cadena para hacer la comparación apoyándonos en la función lower
. Quedaría así:
def eliminar_vocales(cadena):
vocales = 'aeiou'
return ''.join(c for c in cadena if c.lower() not in vocales)
De esta manera, el código es más eficiente al comprobar si cada letra es una vocal (solo buscamos en una cadena de 5 caracteres y no de 10), pero en cambio, hay que hacer una transformación en cada letra. ¿Habremos ganado mucho? (Te dejo pensarlo a ti).
Además, fíjate que esta vez, en lugar de hacer la comprensión de listas para generar una lista, usamos una expresión generadora, consiguiendo el mismo efecto sin poner los corchetes [
y ]
.
Podemos mejorar, de todas formas, esa última comprobación haciendo que, en lugar de buscar en una cadena de texto se busque en un conjunto, que es mucho más rápido.
Lo cambiamos por:
def eliminar_vocales(cadena):
vocales = set('aeiou')
return ''.join(c for c in cadena if c.lower() not in vocales)
Eso sí, ahora que tenemos una forma mucho más rápida de comprobar si una letra es una vocal.
De todas formas,creo que, aunque sea más feo, sería mejor volver a considerar minúsculas y mayúsculas sin hacer la transformación para ganar algo de velocidad (pues la operación de búsqueda en el conjunto sigue siendo igual de rápida).
def eliminar_vocales(cadena):
vocales = set('aeiouAEIOU')
return ''.join(c for c in cadena if c not in vocales)
Sí, ya sé que parece que te estoy mareando, pero quiero que me acompañes en todo el razonamiento para que no te quede ninguna duda.
Vale, hasta ahora, hemos hecho algunas mejoras de eficiencia. Vamos a ver otras opciones de implementación.
Con programación funcional y filter
Si eres más de programación funcional que de comprensiones, podrías pensar en la siguiente solución:
15 conceptos fundamentales que necesitas conocer para aprender y dominar Python
Te voy a hacer cuatro regalos (no uno, no dos, no tres, cuatro) que hablan de estos 15 conceptos fundamentales de Python: mi Tutorial Básico Interactivo de Python, una cheat sheet de Python en español: La Hoja de Referencia de Python, una guía de ChatGPT y Python y 30 ejercicios de Python (es un reto para ti).
Estos regalos son exclusivos para los suscriptores de Código Pitón.
def eliminar_vocales(cadena):
vocales = set('aeiouAEIOU')
return ''.join(filter(lambda c: c not in vocales, cadena))
También me gusta. (Te dejo una pregunta.., ¿cuál será más eficiente, esta solución o la anterior? Haz tus pruebas…).
Claro que entramos en terrenos un poco más avanzados. Lo que hace filter
es filtrar aquellos caracteres de la cadena de texto que no cumplen la condición indicada en la función lambda y dejar los que sí la cumplen.
Por cierto, filter no devuelve una lista, así que debemos convertirlo con el constructor list
.
Con expresiones regulares
Veamos otra solución radicalmente diferente usando expresiones regulares. Sí, ya sé que asustan, pero es otra opción corta y concisa (y clara si entiendes las expresiones regulares, que, en este caso, la que se usa es muy sencilla):
import re
def eliminar_vocales(cadena):
return re.sub(r'[aeiouAEIOU]', '', cadena)
¿Cómo vas?
¿Te das cuenta de todo el petróleo que podemos sacar de un ejercicio que es muy sencillo en apariencia?
Pues venga, que estoy on fire, sigamos aprendiendo. Vamos con unas soluciones un poco más creativas. No necesariamente mejores o más eficientes o más legibles, simplemente diferentes, para aprender otros modos.
En definitiva, para aprender más…
Diccionarios de traducción para cadenas de texto: maketrans
y translate
Usemos diccionarios de traducción:
def eliminar_vocales(cadena):
vocales = 'aeiouAEIOU'
traduccion = str.maketrans('', '', vocales)
return cadena.translate(traduccion)
A ver, que ya ni te explico el código de arriba. Investiga un poco...
Con programación funcional y reduce
Sigamos por la vía funcional y utilicemos reduce:
from functools import reduce
def eliminar_vocales(cadena):
vocales = set('aeiouAEIOU')
return reduce(lambda r, c: r + c if c not in vocales else r, cadena, '')
¿Qué? ¿Cómo lo llevas?
Venga, una más y acabamos con la paliza. Prometido.
Mejoremos la solución tradicional: deque
Iremos a la solución tradicional de vuelta pero utilizaremos deque
(otra estructura de datos interesante y que hay que conocer) para incrementar la eficiencia. el truco aquí está en que la operación append
de deque
es mucha más eficiente que el append
de una lista.
def eliminar_vocales(cadena):
vocales = set('aeiouAEIOU')
resultado = deque()
for c in cadena:
if c not in vocales:
resultado.append(c)
return ''.join(resultado)
Y, ya basta, ¡Juan, por dios, para ya!
Que sí, que sí, que ya paro…
Conclusiones
Bueno, espero que veas lo que se puede ocultar realmente detrás de un ejercicio sencillo en apariencia.
Hay mucha miga en este artículo y mucho que aprender, así que guárdalo en favoritos.
Día que estás fuera, consejo sobre Python que te pierdes.
Antes de suscribirte consulta aquí la
Finalidad de recogida y tratamiento de datos personales: enviarte boletín informativo de Python y comunicaciones comerciales.
Legitimación: tu consentimiento.
Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (GetResponse).
Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control.
Más información: política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal.
Espero que alguna de las soluciones de arriba sea lo que estabas buscando y, por otro lado, espero que hayas podido aprender mucho.
Por cierto, dos cosas.
- Si te gusta esta manera de aprender, realizando los ejercicios de varias maneras, tengo un plan de mejora de Python que es perfecto para ti. Se llama El Gimnasio de Python y en él practicarás un único ejercicio a la semana. Infórmate aquí sobre El Gimnasio de Python.
- Tengo una newsletter gratuita en la que escribo un correo con un consejo de Python al día. Este artículo que ves es el contenido de uno de los correos que envío. Para que veas lo que te puede llegar a tu bandeja de entrada. Aquí te apuntas a la newsletter de Código Pitón.
Y nada más, espero que todo esto te sea muy útil.
Te envío todos los días un consejo para que cada día seas mejor en Python.
Siempre sobre Python y programación.
Más de 2500 personas como tú los reciben cada día.
Día que estás fuera, consejo sobre Python que te pierdes.
Antes de suscribirte consulta aquí la
Finalidad de recogida y tratamiento de datos personales: enviarte boletín informativo de Python y comunicaciones comerciales.
Legitimación: tu consentimiento.
Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (GetResponse).
Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control.
Más información: política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal.