Asking the user for input until they give a valid response
我正在编写一个必须接受用户输入的程序。
1 2 3 4 5 6 | #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.") |
如果用户输入合理的数据,这将按预期工作。
1 2 3 | C:\Python\Projects> canyouvote.py Please enter your age: 23 You are able to vote in the United States! |
但如果他们犯了一个错误,它就会崩溃:
1 2 3 4 5 6 | 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' |
我希望它能再次尝试获取输入,而不是崩溃。这样地:
1 2 3 4 5 | 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! |
我怎样才能做到这一点?如果我还想拒绝像
实现这一点的最简单方法是将
使用Try and Catch检测用户何时输入无法分析的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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.") |
实现您自己的验证规则
如果要拒绝Python可以成功解析的值,可以添加自己的验证逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
结合异常处理和自定义验证
上述两种技术都可以组合成一个循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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.") |
将它全部封装在一个函数中
如果您需要向用户询问许多不同的值,将此代码放入函数中可能会很有用,因此您不必每次都重新键入它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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:") |
把它们放在一起
您可以扩展此思想,以生成非常通用的输入函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 |
使用方法如下:
1 2 | age = sanitised_input("Enter your age:", int, 1, 101) answer = sanitised_input("Enter your answer:", str.lower, range_=('a', 'b', 'c', 'd')) |
常见的陷阱,以及为什么要避免它们冗余使用
这种方法有效,但通常被认为是劣质的:
1 2 3 4 | 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):") |
最初它可能看起来很有吸引力,因为它比
如果您刚刚了解了递归,那么您可能会尝试在
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
这在大多数情况下似乎可以正常工作,但是如果用户输入无效数据的次数足够多,脚本将以
为什么你要做一个
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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.") |
这将导致以下结果:
1 2 3 4 | 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. |
这是可行的,因为年龄永远不会有一个没有意义的价值,代码遵循"业务流程"的逻辑。
虽然公认的答案是惊人的。我还想分享这个问题的快速黑客。(这也解决了消极的年龄问题。)
1 2 3 4 | f=lambda age: (age.isdigit() and ((int(age)>=18 and"Can vote" ) or"Cannot vote")) or \ f(input("invalid input. Try again Please enter your age:")) print(f(input("Please enter your age:"))) |
另外,这个代码是针对python 3.x的。
所以,我最近在搞类似的事情,我想出了下面的解决方案,它使用一种方法来获取拒绝垃圾邮件的输入,甚至在以任何合乎逻辑的方式检查之前。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | def read_single_keypress() -> str: """Waits for a single keypress on stdin. -- from :: https://stackoverflow.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(" sorry, age can only consist of digits.") return print("your age is", x," You can vote!" if x >= 18 else"Sorry! you can't vote") _can_you_vote() |
您可以在这里找到完整的模块。
例子:
1 2 3 4 5 6 7 8 | $ ./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! $ _ |
请注意,此实现的本质是,一旦读取到非数字的内容,它就会关闭stdin。我没有在
您可以将它与同一模块中的
要编辑代码并修复错误:
1 2 3 4 5 6 7 8 9 10 11 | 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") |
1 2 3 4 5 6 7 8 9 10 11 | 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." |
试试这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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') |
使用递归函数的持久用户输入:
弦1 2 3 4 | def askName(): return input("Write your name:").strip() or askName() name = askName() |
整数
1 2 3 4 5 | def askAge(): try: return int(input("Enter your age:")) except ValueError: return askAge() age = askAge() |
最后,问题要求:
1 2 3 4 5 6 7 8 9 10 11 12 | def askAge(): try: return int(input("Enter your age:")) except ValueError: return askAge() age = askAge() responseAge = [ "You are able to vote in the United States!", "You are not able to vote in the United States.", ][int(age < 18)] print(responseAge) |
基于丹尼尔·Q和帕特里克·阿特纳的优秀建议,这里有一个更广义的解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Assuming Python3 import sys class ValidationError(ValueError): # thanks Patrick Artner pass def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None): if onerror==None: onerror = {} while True: try: data = cast(input(prompt)) if not cond(data): raise ValidationError return data except tuple(onerror.keys()) as e: # thanks Daniel Q print(onerror[type(e)], file=sys.stderr) |
我选择了明确的
这可用于获取不同类型的输入,验证条件不同。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # No validation, equivalent to simple input: anystr = validate_input("Enter any string:") # Get a string containing only letters: letters = validate_input("Enter letters:", cond=str.isalpha, onerror={ValidationError:"Only letters, please! <hr><P>您可以将输入语句设为while-true循环,以便它反复请求用户输入,然后在用户输入您想要的响应时中断该循环。您可以使用try和except块来处理无效的响应。</P>[cc lang="python"]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.") |
var变量的作用是,如果用户输入字符串而不是整数,程序将不会返回"您在美国无法投票"。
使用Try-Catch和Never-Ending While循环。若要检查空字符串,请使用if语句检查字符串是否为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | while True: name = input("Enter Your Name ") if not name: print("I did not understood that") continue else: break while True: try: salary = float(input("whats ur salary ")) 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 + " Your salary is" + str(salary) + ' and you will be ' + str(age+1) +' in a Year') |
这将继续要求用户输入数字,直到他们输入有效的数字:
1 2 3 4 5 6 7 8 9 10 11 12 | #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") |
您可以编写更通用的逻辑,允许用户只输入特定的次数,因为在许多实际应用程序中都会出现相同的用例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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. |
使用自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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)) |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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) |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
虽然
1 2 3 4 5 6 7 8 9 10 11 12 | 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.") |
这里有一个更清晰、更通用的解决方案,可以避免重复的if/else块:编写一个函数,在字典中使用(错误、错误提示)对,并使用断言检查所有值。
1 2 3 4 5 6 7 8 9 10 11 | def validate_input(prompt, error_map): while True: try: data = int(input(prompt)) # Insert your non-exception-throwing conditionals here assert data > 0 return data # Print whatever text you want the user to see # depending on how they messed up except tuple(error_map.keys()) as e: print(error_map[type(e)]) |
用途:
1 2 3 | d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', KeyboardInterrupt: 'You can never leave'} user_input = validate_input("Positive number:", d) |
Good question! You can try the following code for this. =)
This code uses ast.literal_eval() to find the data type of the input (
Ask user to input her/his
age .1.1. If
age isfloat orint data type:
Check if
age>=18 . Ifage>=18 , print appropriate output and exit.Check if
0. If 0, print appropriate output and exit. If
age<=0 , ask the user to input a valid number for age again, (i.e. go back to step 1.)1.2. If
age is notfloat orint data type, then ask user to input her/his age again (i.e. go back to step 1.)
Here is the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from ast import literal_eval ''' This function is used to identify the data type of input data.''' def input_type(input_data): try: return type(literal_eval(input_data)) except (ValueError, SyntaxError): return str flag = True while(flag): age = raw_input("Please enter your age:") if input_type(age)==float or input_type(age)==int: if eval(age)>=18: print("You are able to vote in the United States!") flag = False elif eval(age)>0 and eval(age)<18: print("You are not able to vote in the United States.") flag = False else: print("Please enter a valid number as your age.") else: print("Sorry, I didn't understand that.") |
使用"while"语句,直到用户输入一个真值,如果输入值不是数字或是空值,请跳过它,然后再次尝试询问,依此类推。举个例子,我试图真正回答你的问题。如果我们假设我们的年龄在1到150岁之间,那么输入值被接受,否则它是一个错误的值。对于终止程序,用户可以使用0键并将其作为值输入。
Note: Read comments top of code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 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.") |