python - Pedirle al usuario una entrada hasta que den una respuesta válida
validation loops (14)
¿Por qué haría un while True
y luego saldría de este bucle mientras que también puede poner sus requisitos en la declaración while ya que todo lo que quiere es parar una vez que tenga la edad?
age = None
while age is None:
input_value = input("Please enter your age: ")
try:
# try and convert the string input to a number
age = int(input_value)
except ValueError:
# tell the user off
print("{input} is not a number, please enter a number only".format(input=input_value))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Esto daría lugar a lo siguiente:
Please enter your age: *potato*
potato is not a number, please enter a number only
Please enter your age: *5*
You are not able to vote in the United States.
esto funcionará ya que la edad nunca tendrá un valor que no tenga sentido y el código sigue la lógica de su "proceso de negocio"
Estoy escribiendo un programa que debe aceptar la entrada del usuario.
#note: Python 2.7 users should use `raw_input`, the equivalent of 3.X''s `input`
age = int(input("Please enter your age: "))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Esto funciona como se espera si el usuario ingresa datos sensibles.
C:/Python/Projects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!
Pero si cometen un error, entonces se estrella:
C:/Python/Projects> canyouvote.py
Please enter your age: dickety six
Traceback (most recent call last):
File "canyouvote.py", line 1, in <module>
age = int(input("Please enter your age: "))
ValueError: invalid literal for int() with base 10: ''dickety six''
En lugar de estrellarme, me gustaría que intentara obtener la información nuevamente. Me gusta esto:
C:/Python/Projects> canyouvote.py
Please enter your age: dickety six
Sorry, I didn''t understand that.
Please enter your age: 26
You are able to vote in the United States!
¿Cómo puedo lograr esto? ¿Y si también quisiera rechazar valores como -1
, que es un int
válido, pero sin sentido en este contexto?
Aunque la respuesta aceptada es asombrosa. También me gustaría compartir un truco rápido para este problema. (Esto también se ocupa del problema de la edad negativa.)
f=lambda age: (age.isdigit() and ((int(age)>=18 and "Can vote" ) or "Cannot vote")) or /
f(raw_input("invalid input. Try again/nPlease enter your age: "))
print f(raw_input("Please enter your age: "))
PS Este código es para python 2.x y se puede exportar a 3.x cambiando las funciones raw_input e print.
Esto continuará pidiéndole al usuario que ingrese el número hasta que ingrese un número válido:
#note: Python 2.7 users should use raw_input, the equivalent of 3.X''s input
while(1):
try:
age = int(input("Please enter your age: "))
if age >= 18:
print("You are able to vote in the United States!")
break()
else:
print("You are not able to vote in the United States.")
break()
except:
print("Please only enter numbers ")
La forma más sencilla de lograr esto sería poner el método de input
en un bucle while. Use continue
cuando tenga mala entrada y salga del ciclo cuando esté satisfecho.
Cuando su entrada puede generar una excepción
Utilice try and catch para detectar cuándo el usuario ingresa datos que no se pueden analizar.
while True:
try:
# Note: Python 2.x users should use raw_input, the equivalent of 3.x''s input
age = int(input("Please enter your age: "))
except ValueError:
print("Sorry, I didn''t understand that.")
#better try again... Return to the start of the loop
continue
else:
#age was successfully parsed!
#we''re ready to exit the loop.
break
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Implementando tus propias reglas de validación
Si desea rechazar valores que Python pueda analizar con éxito, puede agregar su propia lógica de validación.
while True:
data = input("Please enter a loud message (must be all caps): ")
if not data.isupper():
print("Sorry, your response was not loud enough.")
continue
else:
#we''re happy with the value given.
#we''re ready to exit the loop.
break
while True:
data = input("Pick an answer from A to D:")
if data.lower() not in (''a'', ''b'', ''c'', ''d''):
print("Not an appropriate choice.")
else:
break
Combinación de manejo de excepciones y validación personalizada
Ambas técnicas anteriores se pueden combinar en un bucle.
while True:
try:
age = int(input("Please enter your age: "))
except ValueError:
print("Sorry, I didn''t understand that.")
continue
if age < 0:
print("Sorry, your response must not be negative.")
continue
else:
#age was successfully parsed, and we''re happy with its value.
#we''re ready to exit the loop.
break
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Encapsulando todo en una función
Si necesita pedirle a su usuario muchos valores diferentes, puede ser útil colocar este código en una función, de modo que no tenga que volver a escribirlo cada vez.
def get_non_negative_int(prompt):
while True:
try:
value = int(input(prompt))
except ValueError:
print("Sorry, I didn''t understand that.")
continue
if value < 0:
print("Sorry, your response must not be negative.")
continue
else:
break
return value
age = get_non_negative_int("Please enter your age: ")
kids = get_non_negative_int("Please enter the number of children you have: ")
salary = get_non_negative_int("Please enter your yearly earnings, in dollars: ")
Poniendolo todo junto
Puedes ampliar esta idea para hacer una función de entrada muy genérica:
def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
if min_ is not None and max_ is not None and max_ < min_:
raise ValueError("min_ must be less than or equal to max_.")
while True:
ui = input(prompt)
if type_ is not None:
try:
ui = type_(ui)
except ValueError:
print("Input type must be {0}.".format(type_.__name__))
continue
if max_ is not None and ui > max_:
print("Input must be less than or equal to {0}.".format(max_))
elif min_ is not None and ui < min_:
print("Input must be greater than or equal to {0}.".format(min_))
elif range_ is not None and ui not in range_:
if isinstance(range_, range):
template = "Input must be between {0.start} and {0.stop}."
print(template.format(range_))
else:
template = "Input must be {0}."
if len(range_) == 1:
print(template.format(*range_))
else:
print(template.format(" or ".join((", ".join(map(str,
range_[:-1])),
str(range_[-1])))))
else:
return ui
Con usos como:
age = sanitised_input("Enter your age: ", int, 1, 101)
answer = sanitised_input("Enter your answer: ", str.lower, range_=(''a'', ''b'', ''c'', ''d''))
Trampas comunes y por qué debe evitarlas
El uso redundante de declaraciones de input
redundantes
Este método funciona, pero generalmente se considera estilo pobre:
data = input("Please enter a loud message (must be all caps): ")
while not data.isupper():
print("Sorry, your response was not loud enough.")
data = input("Please enter a loud message (must be all caps): ")
Puede parecer atractivo inicialmente porque es más corto que el método while True
, pero viola el principio de No repetirte a ti mismo del desarrollo de software. Esto aumenta la probabilidad de errores en su sistema. ¿Qué raw_input
si desea realizar una raw_input
de raw_input
a 2.7 cambiando la input
a raw_input
, pero accidentalmente cambiamos solo la primera input
anterior? Es un SyntaxError
está esperando a suceder.
La recursión soplará tu pila
Si acaba de enterarse de la recursión, puede tener la tentación de usarlo en get_non_negative_int
para poder deshacerse del bucle while.
def get_non_negative_int(prompt):
try:
value = int(input(prompt))
except ValueError:
print("Sorry, I didn''t understand that.")
return get_non_negative_int(prompt)
if value < 0:
print("Sorry, your response must not be negative.")
return get_non_negative_int(prompt)
else:
return value
Esto parece funcionar bien la mayor parte del tiempo, pero si el usuario ingresa datos no válidos las veces suficientes, el script terminará con un RuntimeError: maximum recursion depth exceeded
. Puedes pensar que "ningún tonto cometerá 1000 errores seguidos", ¡pero estás subestimando el ingenio de los tontos!
Mientras que un bloque try
/ except
funcionará, una forma mucho más rápida y limpia de realizar esta tarea sería usar str.isdigit()
.
while True:
age = input("Please enter your age: ")
if age.isdigit():
age = int(age)
break
else:
print("Invalid number ''{age}''. Try again.".format(age=age))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Para editar su código y corregir el error:
while True:
try:
age = int(input("Please enter your age: "))
if age >= 18:
print("You are able to vote in the United States!")
break
else:
print("You are not able to vote in the United States.")
break
except ValueError:
print("Please enter a valid response")
Por lo tanto, estuve jugando con algo similar a esto recientemente, y se me ocurrió la siguiente solución, que utiliza una forma de obtener información que rechaza la basura, incluso antes de que se verifique de alguna manera lógica.
read_single_keypress()
cortesía https://.com/a/6599441/4532996
def read_single_keypress() -> str:
"""Waits for a single keypress on stdin.
-- from :: https://.com/a/6599441/4532996
"""
import termios, fcntl, sys, os
fd = sys.stdin.fileno()
# save old state
flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
attrs_save = termios.tcgetattr(fd)
# make raw - the way to do this comes from the termios(3) man page.
attrs = list(attrs_save) # copy the stored version to update
# iflag
attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
| termios.ISTRIP | termios.INLCR | termios. IGNCR
| termios.ICRNL | termios.IXON )
# oflag
attrs[1] &= ~termios.OPOST
# cflag
attrs[2] &= ~(termios.CSIZE | termios. PARENB)
attrs[2] |= termios.CS8
# lflag
attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
| termios.ISIG | termios.IEXTEN)
termios.tcsetattr(fd, termios.TCSANOW, attrs)
# turn off non-blocking
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
# read a single keystroke
try:
ret = sys.stdin.read(1) # returns a single character
except KeyboardInterrupt:
ret = 0
finally:
# restore old state
termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
return ret
def until_not_multi(chars) -> str:
"""read stdin until !(chars)"""
import sys
chars = list(chars)
y = ""
sys.stdout.flush()
while True:
i = read_single_keypress()
_ = sys.stdout.write(i)
sys.stdout.flush()
if i not in chars:
break
y += i
return y
def _can_you_vote() -> str:
"""a practical example:
test if a user can vote based purely on keypresses"""
print("can you vote? age : ", end="")
x = int("0" + until_not_multi("0123456789"))
if not x:
print("/nsorry, age can only consist of digits.")
return
print("your age is", x, "/nYou can vote!" if x >= 18 else "Sorry! you can''t vote")
_can_you_vote()
Puedes encontrar el módulo completo here .
Ejemplo:
$ ./input_constrain.py
can you vote? age : a
sorry, age can only consist of digits.
$ ./input_constrain.py
can you vote? age : 23<RETURN>
your age is 23
You can vote!
$ _
Tenga en cuenta que la naturaleza de esta implementación es que cierra la entrada estándar tan pronto como se lee algo que no es un dígito. No presioné Enter después de a
, pero necesitaba hacerlo después de los números.
Podría combinar esto con la función thismany()
en el mismo módulo para permitir solo, digamos, tres dígitos.
Prueba este: -
def takeInput(required):
print ''ooo or OOO to exit''
ans = raw_input(''Enter: '')
if not ans:
print "You entered nothing...!"
return takeInput(required)
## FOR Exit ##
elif ans in [''ooo'', ''OOO'']:
print "Closing instance."
exit()
else:
if ans.isdigit():
current = ''int''
elif set(''[~!@#$%^&*()_+{}"://']+$'').intersection(ans):
current = ''other''
elif isinstance(ans,basestring):
current = ''str''
else:
current = ''none''
if required == current :
return ans
else:
return takeInput(required)
## pass the value in which type you want [str/int/special character(as other )]
print "input: ", takeInput(''str'')
Puede escribir una lógica más general para permitir al usuario ingresar solo un número específico de veces, ya que el mismo caso de uso surge en muchas aplicaciones del mundo real.
def getValidInt(iMaxAttemps = None):
iCount = 0
while True:
# exit when maximum attempt limit has expired
if iCount != None and iCount > iMaxAttemps:
return 0 # return as default value
i = raw_input("Enter no")
try:
i = int(i)
except ValueError as e:
print "Enter valid int value"
else:
break
return i
age = getValidInt()
# do whatever you want to do.
Puede hacer que la instrucción de entrada sea un ciclo verdadero mientras que pide repetidamente la entrada de los usuarios y luego interrumpa ese ciclo si el usuario ingresa la respuesta que desea. Y puede usar bloques try y except para manejar respuestas inválidas.
while True:
var = True
try:
age = int(input("Please enter your age: "))
except ValueError:
print("Invalid input.")
var = False
if var == True:
if age >= 18:
print("You are able to vote in the United States.")
break
else:
print("You are not able to vote in the United States.")
La variable var es solo para que si el usuario ingresa una cadena en lugar de un entero, el programa no devolverá "No puedes votar en los Estados Unidos".
Una solución más para usar la validación de entrada utilizando un ValidationError
personalizado y una validación de rango (opcional) para entradas de enteros:
class ValidationError(ValueError):
"""Special validation error - its message is supposed to be printed"""
pass
def RangeValidator(text,num,r):
"""Generic validator - raises ''text'' as ValidationError if ''num'' not in range ''r''."""
if num in r:
return num
raise ValidationError(text)
def ValidCol(c):
"""Specialized column validator providing text and range."""
return RangeValidator("Columns must be in the range of 0 to 3 (inclusive)",
c, range(4))
def ValidRow(r):
"""Specialized row validator providing text and range."""
return RangeValidator("Rows must be in the range of 5 to 15(exclusive)",
r, range(5,15))
Uso:
def GetInt(text, validator=None):
"""Aks user for integer input until a valid integer is given. If provided,
a ''validator'' function takes the integer and either raises a
ValidationError to be printed or returns the valid number.
Non integers display a simple error message."""
print()
while True:
n = input(text)
try:
n = int(n)
return n if validator is None else validator(n)
except ValueError as ve:
# prints ValidationErrors directly - else generic message:
if isinstance(ve, ValidationError):
print(ve)
else:
print("Invalid input: ", n)
column = GetInt("Pleased enter column: ", ValidCol)
row = GetInt("Pleased enter row: ", ValidRow)
print( row, column)
Salida:
Pleased enter column: 22
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: -2
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: 2
Pleased enter row: a
Invalid input: a
Pleased enter row: 72
Rows must be in the range of 5 to 15(exclusive)
Pleased enter row: 9
9, 2
Use la frase "while" hasta que el usuario ingrese un valor verdadero y si el valor de entrada no es un número o es un valor nulo, omítalo e intente preguntar de nuevo, y así sucesivamente. En el ejemplo he intentado responder verdaderamente a tu pregunta. Si suponemos que nuestra edad es entre 1 y 150, se acepta el valor de entrada, de lo contrario es un valor incorrecto. Para terminar el programa, el usuario puede usar la tecla 0 e ingresarlo como un valor.
Nota: Lee los comentarios encima del código.
# If your input value is only a number then use "Value.isdigit() == False".
# If you need an input that is a text, you should remove "Value.isdigit() == False".
def Input(Message):
Value = None
while Value == None or Value.isdigit() == False:
try:
Value = str(input(Message)).strip()
except InputError:
Value = None
return Value
# Example:
age = 0
# If we suppose that our age is between 1 and 150 then input value accepted,
# else it''s a wrong value.
while age <=0 or age >150:
age = int(Input("Please enter your age: "))
# For terminating program, the user can use 0 key and enter it as an a value.
if age == 0:
print("Terminating ...")
exit(0)
if age >= 18 and age <=150:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Utilice try catch con nunca terminar mientras loop. Para verificar si hay una cadena en blanco, use la instrucción IF para verificar si la cadena está vacía.
while True:
name = input("Enter Your Name/n")
if not name:
print("I did not understood that")
continue
else:
break
while True:
try:
salary = float(input("whats ur salary/n"))
except ValueError:
print("I did not understood that")
continue
else:
break
while True:
try:
print("whats ur age?")
age = int(float(input()))
except ValueError:
print("I did not understood that")
continue
else:
break
print("Hello "+ name + "/nYour salary is " + str(salary) + ''/nand you will be '' + str(age+1) +'' in a Year'')
def validate_age(age):
if age >=0 :
return True
return False
while True:
try:
age = int(raw_input("Please enter your age:"))
if validate_age(age): break
except ValueError:
print "Error: Invalid age."