要求用户input,直到他们提供有效的答复

我正在编写一个程序,必须接受来自用户的input。

#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.") 

如果用户input合理的数据,这将按预期工作。

 C:\Python\Projects> canyouvote.py Please enter your age: 23 You are able to vote in the United States! 

但如果他们犯了一个错误,那就崩溃了:

 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' 

而不是崩溃,我希望它再次尝试获取input。 喜欢这个:

 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! 

我怎样才能做到这一点? 如果我也想拒绝像-1这样的值,这是一个有效的int ,但在这种情况下是荒谬的?

最简单的方法是将input方法放在while循环中。 当你input错误的时候continue使用,当你满意的时候跳出循环。

当您的input可能会引发例外

使用try和catch来检测用户何时input无法分析的数据。

 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.") 

实施您自己的validation规则

如果你想拒绝Python可以成功parsing的值,你可以添加你自己的validation逻辑。

 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 

结合exception处理和自定义validation

上述两种技术都可以组合成一个循环。

 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.") 

将其封装在一个函数中

如果您需要向用户提出许多不同的值,将这些代码放在一个函数中可能会很有用,所以您不必每次都重新input。

 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: ") 

把它放在一起

你可以扩展这个想法来做一个非常通用的input函数:

 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 

用法如下:

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

常见的陷阱,为什么你应该避免它们

冗余input语句的冗余使用

这种方法工作,但通常被认为是不好的风格:

 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): ") 

它最初可能看起来很吸引人,因为它比while True方法更短,但这违反了软件开发的“ 不要重复”原则。 这增加了您的系统中的错误的可能性。 如果要通过将input更改为raw_inputraw_input移植到2.7,但意外更改了上面的第一个input ? 这是一个正在等待发生的SyntaxError

recursion会吹你的堆栈

如果你刚刚学习了recursion,你可能会试图在get_non_negative_int使用它,所以你可以处理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 

这似乎大多数情况下正常工作,但如果用户input足够多的时间,脚本将终止RuntimeError: maximum recursion depth exceeded 。 你可能会认为“愚人不会连续犯1000次错误”,但是你低估了愚人的聪明才智!

为什么你会做一段while True ,然后摆脱这个循环,而你也可以把你的要求放在while语句中,因为你想要的只是一旦你有年龄就停下来?

 age = None while age is None: input_value = raw_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.") 

这将导致以下结果:

 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. 

这将工作,因为年龄将永远不会有一个价值无益的代码遵循您的“业务stream程”的逻辑,

虽然接受的答案是惊人的。 我也想分享一下这个问题。 (这也照顾到负面的年龄问题。)

 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这个代码是用于python 2.x的,通过改变raw_input和print函数可以导出到3.x。

所以,我最近搞了类似的东西,我提出了下面的解决scheme,它采用了一种方法来获取拒绝垃圾的input,甚至在以任何合理的方式进行检查之前。

read_single_keypress()礼貌https://stackoverflow.com/a/6599441/4532996

 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("\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() 

你可以在这里find完整的模块。

例:

 $ ./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! $ _ 

请注意,这个实现的本质是一旦读取了不是数字的东西,就会closuresstdin。 之后我没有进入,但是我需要在数字之后。

你可以把它和thismany()函数合并到同一个模块中,只允许三位数字。

 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." 

试试这个:

 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') 

编辑您的代码并修复错误:

 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") 

您可以编写更一般的逻辑来允许用户只input特定的次数,因为在许多现实应用程序中出现相同的用例。

 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. 

当一个try / except块可以工作时,完成这个任务的更快更str.isdigit()是使用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.") 

使用“while”语句,直到用户input一个真值,如果input值不是一个数字,或者它是一个空值,则跳过它并尝试再次询问等等。 在例子中,我试图回答你的问题。 如果我们假设我们的年龄在1到150之间,那么input的价值就被接受了,否则就是错误的价值。 对于终止程序,用户可以使用0键并将其作为一个值input。

注意:阅读注释顶部的代码。

 # 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.")