python - Asking the user for input until they give a valid response -


i writing program must accept input user.

#note: python 2.7 users should use `raw_input`, equivalent of 3.x's `input` age = int(input("please enter age: ")) if age >= 18:      print("you able vote in united states!") else:     print("you not able vote in united states.") 

this works expected if user enters sensible data.

c:\python\projects> canyouvote.py please enter age: 23 able vote in united states! 

but if make mistake, crashes:

c:\python\projects> canyouvote.py please enter age: dickety 6 traceback (most recent call last):   file "canyouvote.py", line 1, in <module>     age = int(input("please enter age: ")) valueerror: invalid literal int() base 10: 'dickety six' 

instead of crashing, try getting input again. this:

c:\python\projects> canyouvote.py please enter age: dickety 6 sorry, didn't understand that. please enter age: 26 able vote in united states! 

how can accomplish this? if wanted reject values -1, valid int, nonsensical in context?

the simplest way accomplish put input method in while loop. use continue when bad input, , break out of loop when you're satisfied.

when input might raise exception

use try , catch detect when user enters data can't parsed.

while true:     try:         # note: python 2.x users should use raw_input, equivalent of 3.x's input         age = int(input("please enter age: "))     except valueerror:         print("sorry, didn't understand that.")         #better try again... return start of loop         continue     else:         #age parsed!         #we're ready exit loop.         break if age >= 18:      print("you able vote in united states!") else:     print("you not able vote in united states.") 

implementing own validation rules

if want reject values python can parse, can add own validation logic.

while true:     data = input("please enter loud message (must caps): ")     if not data.isupper():         print("sorry, response not loud enough.")         continue     else:         #we're happy value given.         #we're ready exit loop.         break  while true:     data = input("pick answer d:")     if data.lower() not in ('a', 'b', 'c', 'd'):         print("not appropriate choice.")     else:         break 

combining exception handling , custom validation

both of above techniques can combined 1 loop.

while true:     try:         age = int(input("please enter age: "))     except valueerror:         print("sorry, didn't understand that.")         continue      if age < 0:         print("sorry, response must not negative.")         continue     else:         #age parsed, , we're happy value.         #we're ready exit loop.         break if age >= 18:      print("you able vote in united states!") else:     print("you not able vote in united states.") 

encapsulating in function

if need ask user lot of different values, might useful put code in function, don't have retype every time.

def get_non_negative_int(prompt):     while true:         try:             value = int(input(prompt))         except valueerror:             print("sorry, didn't understand that.")             continue          if value < 0:             print("sorry, response must not negative.")             continue         else:             break     return value  age = get_non_negative_int("please enter age: ") kids = get_non_negative_int("please enter number of children have: ") salary = get_non_negative_int("please enter yearly earnings, in dollars: ") 

putting together

you can extend idea make generic input function:

def sanitised_input(prompt, type_=none, min_=none, max_=none, range_=none):     if min_ not none , max_ not none , max_ < min_:         raise valueerror("min_ must less or equal max_.")     while true:         ui = input(prompt)         if type_ not none:             try:                 ui = type_(ui)             except valueerror:                 print("input type must {0}.".format(type_.__name__))                 continue         if max_ not none , ui > max_:             print("input must less or equal {0}.".format(max_))         elif min_ not none , ui < min_:             print("input must greater or equal {0}.".format(min_))         elif range_ not none , ui not in range_:             if isinstance(range_, range):                 template = "input must between {0.start} , {0.stop}."                 print(template.format(range_))             else:                 template = "input must {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 

with usage such as:

age = sanitised_input("enter age: ", int, 1, 101) answer = sanitised_input("enter answer", str.lower, range_=('a', 'b', 'c', 'd')) 

common pitfalls, , why should avoid them

the redundant use of redundant input statements

this method works considered poor style:

data = input("please enter loud message (must caps): ") while not data.isupper():     print("sorry, response not loud enough.")     data = input("please enter loud message (must caps): ") 

it might attractive because it's shorter while true method, violates don't repeat yourself principle of software development. increases likelihood of bugs in system. if want backport 2.7 changing input raw_input, accidentally change first input above? it's syntaxerror waiting happen.

recursion blow stack

if you've learned recursion, might tempted use in get_non_negative_int can dispose of while loop.

def get_non_negative_int(prompt):     try:         value = int(input(prompt))     except valueerror:         print("sorry, didn't understand that.")         return get_non_negative_int(prompt)      if value < 0:         print("sorry, response must not negative.")         return get_non_negative_int(prompt)     else:         return value 

this appears work fine of time, if user enters invalid data enough times, script terminate runtimeerror: maximum recursion depth exceeded. may think "no fool make 1000 mistakes in row", you're underestimating ingenuity of fools!


Comments

Popular posts from this blog

PySide and Qt Properties: Connecting signals from Python to QML -

c# - DevExpress.Wpf.Grid.InfiniteGridSizeException was unhandled -

scala - 'wrong top statement declaration' when using slick in IntelliJ -