Pedir un Valor al Usuario hasta que sea Válido en Python

Pedir valor al usuario hasta que sea válido en Python

En muchas situaciones necesitamos pedir un valor al usuario de manera repetida, una y otra vez, hasta que el usuario proporciona un valor válido, de lo contrario el programa no puede continuar. Te explico en este post cómo lograr esto teniendo en cuenta que todo lo que cabe dentro de «valor válido» es muy amplio.

Una manera sencilla y directa de pedir un valor al usuario hasta que sea válido en Python es usar la función input para solicitar el valor dentro de un bucle y comprobar su validez para tenerla en cuenta en la condición de control del bucle:
while not valido do:
   v = input()
   valido = v > 0

Aunque esta solución es muy sencilla, es importante tener en cuenta que la comprobación de validez nunca es tan simple como parece. Debemos asegurarnos de que el valor tiene el tipo correcto. Una vez hecho esto, hay que determinar si el valor está dentro del conjunto de valores válidos o si tiene el formato adecuado. Veremos todo esto a continuación.

Pedir un valor al usuario repetidamente

La forma más sencilla de hacer esto es con un bucle. Lo ideal sería usar un bucle del estilo do … while que permite la ejecución del cuerpo del bucle al menos una vez antes de comprobar la condición, pero Python no dispone de este tipo de bucles.

Debemos usar un bucle while. La característica principal de este bucle es que el cuerpo del bucle se repite mientras la condición sea cierta y que, además, la condición se comprueba antes de la ejecución del cuerpo del bucle. Como necesitamos que el código que hay dentro del bucle se ejecute al menos una vez, tenemos que forzar que dicha condición sea cierta antes de nada.

Como condición vamos a utilizar una variable valido que almacene un valor booleano (cierto o falso) y que vamos a inicializar a False. Esto tiene sentido porque antes de pedir el valor al usuario por primera vez podemos asumir que el valor que tenemos no es válido.

A continuación escribiremos nuestro bucle en el que ubicaremos la solicitud de un valor por teclado al usuario. Una vez que el usuario haya introducido el dato pasamos a comprobar si es cierto. No vamos a centrarnos de momento en cómo realizar esa comprobación, primero quiero centrarme en la estructura de control para pedir el valor y realizar la comprobación. Vamos, entonces, a suponer que tenemos una función validar que recibe el dato y nos dice si el valor es válido (True) o si no lo es (False).

Actualizaremos nuestra variable valido con el valor booleano que haya devuelto la función de validar. En cuanto termine la ejecución del cuerpo del bucle se evaluará de nuevo la condición, si el valor introducido es válido el bucle terminará y continuará la ejecución del programa, pero si el valor no es válido el cuerpo del bucle volverá a ejecutarse de forma que se pedirá de nuevo el valor al usuario y se realizará nuevamente la validación.

Adicionalmente, vamos a permitir que el usuario sea consciente de que el valor que ha introducido no es válido para que pueda entender por qué le pedimos nuevamente un valor.

Te muestro a continuación el código que recoge esta explicación, que es más sencillo de lo que parece:

#variable que indica si el valor es válido
#inicialmente no lo es
valido = False

#función para validar un dato
def validar(valor):
	return False #de momento está sin implementar y devolvemos siempre False

#mensaje para que el usuario sepa que le solicitamos un valor
print('Introduzca un valor, por favor: ', end='')

#bucle para pedir el valor
while not valido:
	valor = input()
	valido = validar(valor)
	if not valido:
		print('Lo siento, el valor no es válido, vuelva a intentarlo: ', end='')

#a partir de aquí sabemos que el valor es válido y ya podemos utilizarlo
print(f'El valor válido es {valor}.')

Si ejecutamos este código nos encontraremos con que el programa nos solicita de manera indefinida y repetidamente un valor una y otra vez y no acaba nunca. Este es precisamente el comportamiento que queremos, y es debido a que la función de validar siempre nos dice que el valor no es válido. Te muestro a continuación una ejecución de la estructura general:

Introduzca un valor, por favor: 10
Lo siento, el valor no es válido, vuelva a intentarlo: 15
Lo siento, el valor no es válido, vuelva a intentarlo: 20
Lo siento, el valor no es válido, vuelva a intentarlo: -5
Lo siento, el valor no es válido, vuelva a intentarlo: 100
Lo siento, el valor no es válido, vuelva a intentarlo: 

Con esta estructura general para solicitar un valor solo nos queda implementar la función de validar para realizar las comprobaciones que necesitamos. Te voy a contar varios ejemplos.

Comprobar si un valor solicitado al usuario es correcto

La comprobación más sencilla es la de determinar si un valor está dentro de un conjunto de valores permitidos. Por ejemplo, queremos comprobar que un número entero esté dentro de un rango permitido, digamos, de 0 a 100.

Así, en la función validar solo tenemos que preocuparnos de mirar si el valor se encuentra comprendido en ese rango. Hay que tener en cuenta que la función input devuelve una cadena de texto, por lo que previamente tienes que convertir el valor introducido de texto a entero. Ejemplo:

def validar(valor):
	entero = int(valor)
	return entero >= 0 and entero <= 100

De esta manera, si ejecuto la estructura general con esta función de validar obtengo el siguiente comportamiento:

Introduzca un valor entre 0 y 100, por favor: -5
Lo siento, el valor no es válido, vuelva a intentarlo: -1
Lo siento, el valor no es válido, vuelva a intentarlo: 101
Lo siento, el valor no es válido, vuelva a intentarlo: 50
El valor válido es 50.

¡Justo lo que querías! Ya ves que es bien sencillo. Con esto ya puedes pedir tantos valores como quieras, solo tienes que crear una función de validación para cada dato que vayas a pedir al usuario.

Pero espera, que no todo iba a ser tan bonito. Has de tener en cuenta que el usuario puede equivocarse y tal vez no introduzca un número entero. De ser así, nuestro programa ya no funcionará y hay que modificarlo levemente. Lo interesante de tenerlo así estructurado es que solo debemos modificar la función de validar.

Comprobar si el tipo de un valor es correcto

Como te decía, si pides un número entero al usuario y este escribe un texto o un número decimal, al intentar convertir la entrada del usuario a número entero obtendremos un error y el programa romperá su ejecución. Para evitar esto es necesario que incorpores una comprobación de tipo en la función de validación.

Es importante destacar que lo que queremos hacer es comprobar si el contenido recogido en una variable de tipo texto o string es convertible a una variable del tipo que nos interesa, y no simplemente comprobar si una variable es de un tipo determinado. Esto es debido a que lo que obtenemos del usuario a través de la función input es un string.

Una posible opción aquí es intentar la conversión al tipo deseado. Si la conversión tiene éxito continuamos con el programa. Si no, tenemos que recoger el error (capturar la excepción) para evitar que el programa rompa la ejecución y devolver False como resultado de la validación.

Para capturar el error utilizamos una construcción del tipo try ... except, que se encarga de intentar la ejecución de un fragmento de código (bloque try). Si dicho fragmento genera un error, se ejecutará otro código encargado de gestionar ese error (bloque except). Te lo ilustro con un ejemplo donde intento la conversión de una variable de tipo str a otra de tipo float:

#pedimos un valor al usuario
valor = input()

try:
	entero = float(valor) #intentamos la conversión
except (ValueError):
	print('Error: valor no es un número decimal') #gestionamos el error

Integrando esta comprobación en la función de validación, esta nos quedaría como sigue, suponiendo que necesitamos un valor decimal entre 0 y 1:

def validar(valor):
	try:
		decimal = float(valor)
	except ValueError:
		return False #si la conversión falla devolvemos False

	return decimal >= 0 and decimal <= 1

Hasta aquí ya tienes las herramientas necesarias para solicitar valores numéricos al usuario, pero muchas veces sucede que el dato que necesitamos es de tipo texto y necesitamos comprobar que su formato es correcto. Un ejemplo muy típico sería comprobar que el usuario ha introducido una dirección de correo electrónico válida o una contraseña que cumple con unos requisitos mínimos de seguridad. Lo vemos a continuación.

Comprobar si el formato de un valor es correcto

Existen una serie de comprobaciones de formato estándar para cadenas de texto que se pueden utilizar en la función de validación. Te cito algunas útiles:

  • Comprobar la longitud de una cadena de texto. Función len. Uso: len(texto).
  • Comprobar si una cadena de texto está en minúsculas. Función islower. Uso: texto.islower().
  • Comprobar si una cadena de texto está en mayúsculas. Función isupper. Uso: texto.isupper().
  • Comprobar si una cadena comienza con un texto determinado. Función: startswith: Uso: texto.startswith(subtexto).
  • Comprobar si una cadena termina con un texto determinado. Función: endswith: Uso: texto.endswith(subtexto).

Estas funciones (y otras similares) utilizadas junto con la notación de porciones para acceder a subcadenas de texto permiten hacer gran tipo de comprobaciones, pero que no dejan de ser sencillas o muy manuales. Si necesitamos hacer algo un poco más complejo el código puede complicarse bastante.

Mi consejo en este caso es hacer uso de las expresiones regulares. Tratar aquí el tema de las expresiones regulares no sería viable, pues el artículo se extendería demasiado. De todas formas aquí te dejo una pequeña introducción a las expresiones regulares en Python para que te vayas familiarizando con ellas.

Básicamente se trata de definir el patrón de formato que debe seguir la entrada del usuario. Por ejemplo, si necesitamos que el usuario introduzca una dirección de correo electrónico la entrada debe tener un conjunto de letras, números y caracteres permitidos sin espacios (el nombre del usuario), una arroba (@), otro conjunto de letras y números sin espacios, (el nombre del dominio) un punto y otro conjunto de al menos dos letras sin espacios (la extensión del dominio).

Como te decía, intentar dicha comprobación manualmente puede ser un tanto complicado, pero las expresiones regulares nos facilitan la tarea. Primero definiremos el patrón utilizando los siguientes elementos teniendo en cuenta que solo vamos a permitir como símbolos el guión bajo y el punto:

  • \w Representa cualquier dígito o letra, ya sea mayúscula o minúscula o un guión bajo (_).
  • \. Representa el punto (.)
  • + Representa una repetición o más del símbolo anterior.
  • ( y ) Permiten agrupar los símbolos
  • | Permite aplicar un símbolo u otro.
  • ^ Representa el principio del texto.
  • $ Representa el final del texto.

Así, el patrón que podríamos construir, asegurándonos de que no se comienza con un punto en los nombres de usuario o dominio, sería el siguiente:

patron_email = '^\w(\w|\.)*@\w(\w|\.)*\.\w\w+$'
Este patrón no es perfecto y no sirve para representar todos los posibles e-mails, pues hay algunos casos que no se consideran. Se ha simplificado en este post por motivos didácticos.

Después usamos, utilizando la librería estándar re (por sus siglas en inglés regular expressions), la función search que busca una subcadena de texto que cumpla el patrón deseado. Como nuestro patrón debe empezar a principio del texto y terminar al final solo puede haber una coincidencia y que esta sea el texto completo. Si lo cumple nos devuelve un objeto con el texto que encaja con el patrón y si no, devuelve None. Así, nuestra función de validar, integrada ya en la estructura general queda como sigue:

import re

def validar(valor):
	patron_email = '^\w(\w|\.)*@\w(\w|\.)*\.\w\w+$'
	resultado = re.search(patron_email, valor)
	return resultado


valido = False

print('Introduzca una dirección de correo electrónico: ', end='')

while not valido:
	valor = input()
	valido = validar(valor)
	if not valido:
		print('Lo siento, la dirección no es válida, vuelva a intentarlo: ', end='')

print(f'El valor válido es {valor}.')

Si ejecutamos este código obtenemos la siguiente salida:

Introduzca una dirección de correo electrónico: 1
Lo siento, la dirección no es válida, vuelva a intentarlo: juan
Lo siento, la dirección no es válida, vuelva a intentarlo: @codigopiton.com
Lo siento, la dirección no es válida, vuelva a intentarlo: prueba con [email protected]
Lo siento, la dirección no es válida, vuelva a intentarlo: juan_codigopiton.com
Lo siento, la dirección no es válida, vuelva a intentarlo: [email protected]
El valor válido es [email protected]

Espero haberte dado aquí las herramientas suficientes como para poder pedir valores al usuario en Python y realizar validaciones como para permitir que tus programas sean todo lo robustos que quieres.

La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
Consigue trucos, consejos y actualizaciones y, por supuesto, la Hoja de Referencia de Python gratis.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Laura Otero Moreira. Finalidad de la recogida y tratamiento de los 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 (MailRelay). 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 en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.