I am writing a program that must accept input from the user.
我正在编写一个必须接受用户输入的程序。
#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.")
This works as expected if the user enters sensible data.
如果用户输入合理数据,这将按预期工作。
C:\Python\Projects> canyouvote.pyPlease enter your age: 23You are able to vote in the United States!
But if they make a mistake, then it crashes:
但如果他们犯了错误,那就崩溃了:
C:\Python\Projects> canyouvote.pyPlease enter your age: dickety sixTraceback (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'
Instead of crashing, I would like it to try getting the input again. Like this:
而不是崩溃,我希望它再次尝试获取输入。像这样:
C:\Python\Projects> canyouvote.pyPlease enter your age: dickety sixSorry, I didn't understand that.Please enter your age: 26You are able to vote in the United States!
How can I accomplish this? What if I also wanted to reject values like -1
, which is a valid int
, but nonsensical in this context?
我怎么能做到这一点?如果我还想拒绝-1这样的值,这是一个有效的int,但在这个上下文中是无意义的,该怎么办?
17 个解决方案
#1
The simplest way to accomplish this would be to put the input
method in a while loop. Use continue
when you get bad input, and break
out of the loop when you're satisfied.
实现此目的的最简单方法是将输入方法放在while循环中。当输入错误时使用continue,当你满意时跳出循环。
When Your Input Might Raise an Exception
Use try and catch to detect when the user enters data that can't be parsed.
使用try和catch检测用户何时输入无法解析的数据。
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. breakif age >= 18: print("You are able to vote in the United States!")else: print("You are not able to vote in the United States.")
Implementing Your Own Validation Rules
If you want to reject values that Python can successfully parse, you can add your own validation logic.
如果要拒绝Python可以成功解析的值,可以添加自己的验证逻辑。
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. breakwhile 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
Combining Exception Handling and Custom Validation
Both of the above techniques can be combined into one loop.
上述两种技术都可以组合成一个循环。
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. breakif age >= 18: print("You are able to vote in the United States!")else: print("You are not able to vote in the United States.")
Encapsulating it All in a Function
If you need to ask your user for a lot of different values, it might be useful to put this code in a function, so you don't have to retype it every time.
如果您需要向用户询问许多不同的值,将此代码放在函数中可能很有用,因此您不必每次都重新键入它。
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 valueage = 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: ")
Putting It All Together
You can extend this idea to make a very generic input function:
您可以扩展这个想法,以创建一个非常通用的输入函数:
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
With usage such as:
使用如下:
age = sanitised_input("Enter your age: ", int, 1, 101)answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))
Common Pitfalls, and Why you Should Avoid Them
The Redundant Use of Redundant input
Statements
This method works but is generally considered poor style:
这种方法有效但通常被认为是不好的风格:
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): ")
It might look attractive initially because it's shorter than the while True
method, but it violates the Don't Repeat Yourself principle of software development. This increases the likelihood of bugs in your system. What if you want to backport to 2.7 by changing input
to raw_input
, but accidentally change only the first input
above? It's a SyntaxError
just waiting to happen.
它最初看起来很有吸引力,因为它比True方法短,但它违反了不要重复自己的软件开发原则。这会增加系统中出现错误的可能性。如果您想通过将输入更改为raw_input来向后移植到2.7,但是只是意外地更改了上面的第一个输入,该怎么办?这是一个等待发生的SyntaxError。
Recursion Will Blow Your Stack
If you've just learned about recursion, you might be tempted to use it in get_non_negative_int
so you can dispose of the while loop.
如果你刚刚学习了递归,你可能会想在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
This appears to work fine most of the time, but if the user enters invalid data enough times, the script will terminate with a RuntimeError: maximum recursion depth exceeded
. You may think "no fool would make 1000 mistakes in a row", but you're underestimating the ingenuity of fools!
这似乎在大多数情况下都能正常工作,但如果用户输入的数据足够多次,脚本将以RuntimeError终止:超出最大递归深度。你可能会认为“没有傻瓜会连续犯1000个错误”,但你低估了傻瓜的聪明才智!
#2
Why would you do a while True
and then break out of this loop while you can also just put your requirements in the while statement since all you want is to stop once you have the age?
你为什么要做一段时间才真正然后突破这个循环,你也可以把你的要求放在while语句中,因为你想要的就是在你有年龄后停止?
age = Nonewhile 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.")
This would result in the following:
这将导致以下结果:
Please enter your age: *potato*potato is not a number, please enter a number onlyPlease enter your age: *5*You are not able to vote in the United States.
this will work since age will never have a value that will not make sense and the code follows the logic of your "business process"
这将有效,因为年龄永远不会有一个没有意义的价值,代码遵循你的“业务流程”的逻辑
#3
Though the accepted answer is amazing. I would also like to share a quick hack for this problem. (This takes care of the negative age problem as well.)
虽然接受的答案是惊人的。我还想分享这个问题的快速入侵。 (这也解决了负面年龄问题。)
f=lambda age: (age.isdigit() and ((int(age)>=18 and "Can vote" ) or "Cannot vote")) or \f(input("invalid input. Try again\nPlease enter your age: "))print(f(input("Please enter your age: ")))
P.S. This code is for python 3.x.
附:此代码适用于python 3.x.
#4
So, I was messing around with something similar to this recently, and I came up with the following solution, which uses a way of getting input that rejects junk, before it's even checked in any logical way.
所以,我最近搞乱了类似于此的东西,我想出了以下解决方案,它使用一种获取输入的方法来拒绝垃圾,甚至在以任何逻辑方式检查之前。
read_single_keypress()
courtesy https://*.com/a/6599441/4532996
read_single_keypress()礼貌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 retdef 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 ydef _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()
You can find the complete module here.
你可以在这里找到完整的模块。
Example:
$ ./input_constrain.pycan you vote? age : asorry, age can only consist of digits.$ ./input_constrain.py can you vote? age : 23<RETURN>your age is 23You can vote!$ _
Note that the nature of this implementation is it closes stdin as soon as something that isn't a digit is read. I didn't hit enter after a
, but I needed to after the numbers.
请注意,此实现的性质是,只要读取非数字的内容,它就会关闭stdin。我之后没有按进入,但我需要在数字之后。
You could merge this with the thismany()
function in the same module to only allow, say, three digits.
您可以将它与同一模块中的thismany()函数合并为仅允许三位数。
#5
def validate_age(age): if age >=0 : return True return Falsewhile True: try: age = int(raw_input("Please enter your age:")) if validate_age(age): break except ValueError: print "Error: Invalid age."
#6
Building upon Daniel Q's and Patrick Artner's excellent suggestions,here is an even more generalized solution.
基于Daniel Q和Patrick Artner的出色建议,这是一个更加通用的解决方案。
# Assuming Python3import sysclass ValidationError(ValueError): # thanks Patrick Artner passdef 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)
I opted for explicit if
and raise
statements instead of an assert
,because assertion checking may be turned off,whereas validation should always be on to provide robustness.
我选择显式if和raise语句而不是断言,因为断言检查可能会被关闭,而验证应始终打开以提供稳健性。
This may be used to get different kinds of input,with different validation conditions.For example:
这可用于获得不同类型的输入,具有不同的验证条件。例如:
# 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!"})# Get a float in [0, 100]:percentage = validate_input("Percentage? ", cast=float, cond=lambda x: 0.0<=x<=100.0, onerror={ValidationError: "Must be between 0 and 100!", ValueError: "Not a number!"})
Or, to answer the original question:
或者,回答原始问题:
age = validate_input("Please enter your age: ", cast=int, cond=lambda a:0<=a<150, onerror={ValidationError: "Enter a plausible age, please!", ValueError: "Enter an integer, please!"})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.")
#7
Try this one:-
试试这个: -
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')
#8
While a try
/except
block will work, a much faster and cleaner way to accomplish this task would be to use str.isdigit()
.
虽然try / except块可以工作,但完成此任务的更快更简洁的方法是使用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.")
#9
One more solution for using input validation using a customized ValidationError
and a (optional) range validation for integer inputs:
使用定制ValidationError和(可选)范围验证对整数输入使用输入验证的另一种解决方案:
class ValidationError(ValueError): """Special validation error - its message is supposed to be printed""" passdef 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))
Usage:
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)
Output:
Pleased enter column: 22Columns must be in the range of 0 to 3 (inclusive)Pleased enter column: -2Columns must be in the range of 0 to 3 (inclusive)Pleased enter column: 2Pleased enter row: aInvalid input: aPleased enter row: 72Rows must be in the range of 5 to 15(exclusive)Pleased enter row: 9 9, 2
#10
Good question! You can try the following code for this. =)
好问题!您可以尝试以下代码。 =)
This code uses ast.literal_eval() to find the data type of the input (age
). Then it follows the following algorithm:
此代码使用ast.literal_eval()来查找输入的数据类型(年龄)。然后它遵循以下算法:
Ask user to input her/his
age
.要求用户输入她/他的年龄。
1.1. If
age
isfloat
orint
data type:1.1。如果age是float或int数据类型:
Check if
age>=18
. Ifage>=18
, print appropriate output and exit.检查年龄> = 18。如果年龄> = 18,打印适当的输出并退出。
Check if
0<age<18
. If0<age<18
, print appropriate output and exit.检查0 <年龄<18岁。如果0
If
age<=0
, ask the user to input a valid number for age again, (i.e. go back to step 1.)如果年龄<= 0,请要求用户再次输入年龄的有效数字(即返回步骤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.)1.2。如果age不是float或int数据类型,则要求用户再次输入她/他的年龄(即返回步骤1)。
Here is the code.
这是代码。
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 strflag = Truewhile(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.")
#11
Persistent user input using recursive function:
使用递归函数的持久用户输入:
String
def askName(): return input("Write your name: ").strip() or askName()name = askName()
Integer
def askAge(): try: return int(input("Enter your age: ")) except ValueError: return askAge()age = askAge()
and finally, the question requirement:
最后,问题要求:
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)
#12
Functional approach or "look mum no loops!":
from itertools import chain, repeatprompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies))print(valid_response)
Enter a number: aNot a number! Try again: bNot a number! Try again: 11
or if you want to have a "bad input" message separated from an input prompt as in other answers:
或者如果您希望将“错误输入”消息与输入提示分开,如在其他答案中那样:
prompt_msg = "Enter a number: "bad_input_msg = "Sorry, I didn't understand that."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies))print(valid_response)
Enter a number: aSorry, I didn't understand that.Enter a number: bSorry, I didn't understand that.Enter a number: 11
How does it work?
-
This combination ofprompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
itertools.chain
anditertools.repeat
will create an iterator which will yield strings"Enter a number: "
once, and"Not a number! Try again: "
an infinite number of times:for prompt in prompts: print(prompt)
Enter a number: Not a number! Try again: Not a number! Try again: Not a number! Try again: # ... and so on
-
replies = map(input, prompts)
- heremap
will apply all theprompts
strings from the previous step to theinput
function. E.g.:for reply in replies: print(reply)
Enter a number: aaNot a number! Try again: 11Not a number! Try again: it doesn't care nowit doesn't care now# and so on...
- We use
filter
andstr.isdigit
to filter out those strings that contain only digits:only_digits = filter(str.isdigit, replies)for reply in only_digits: print(reply)
And to get only the first digits-only string we useEnter a number: aNot a number! Try again: 11Not a number! Try again: 22Not a number! Try again: bNot a number! Try again: # and so on...
next
.
prompts = chain([“输入数字:”],重复(“不是数字!再试一次:”))itertools.chain和itertools.repeat的这个组合将创建一个迭代器,它将产生字符串“输入数字:”一次,和“不是数字!再试一次:”无数次:提示提示:打印(提示)输入数字:不是数字!再试一次:不是数字!再试一次:不是数字!再试一次:#...依此类推
replies = map(输入,提示) - 这里的地图将把上一步的所有提示字符串应用到输入函数。例如:回复中的回复:print(回复)输入一个数字:aaNot a number!再试一次:11没有数字!再试一次:它不关心现在不关心#等等......
我们使用filter和str.isdigit过滤掉那些只包含数字的字符串:only_digits = filter(str.isdigit,reply)for only in again_digits:print(reply)输入一个数字:a不是数字!再试一次:11没有数字!再试一次:22没有数字!再试一次:b不要一个号码!再试一次:#等等......并且只获取我们下一步使用的第一个数字字符串。
Other validation rules:
-
String methods: Of course you can use other string methods like
str.isalpha
to get only alphabetic strings, orstr.isupper
to get only uppercase. See docs for the full list.字符串方法:当然,您可以使用其他字符串方法(如str.isalpha)仅获取字母字符串,或者使用str.isupper仅获取大写字母。有关完整列表,请参阅文档。
-
Membership testing:
There are several different ways to perform it. One of them is by using__contains__
method:成员资格测试:有几种不同的方法可以执行它。其中一个是使用__contains__方法:
from itertools import chain, repeatfruits = {'apple', 'orange', 'peach'}prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))replies = map(input, prompts)valid_response = next(filter(fruits.__contains__, replies))print(valid_response)
Enter a fruit: 1I don't know this one! Try again: fooI don't know this one! Try again: appleapple
-
Numbers comparison:
There are useful comparison methods which we can use here. For example, for__lt__
(<
):数字比较:我们可以在这里使用有用的比较方法。例如,对于__lt __(<):
from itertools import chain, repeatprompts = chain(["Enter a positive number:"], repeat("I need a positive number! Try again:"))replies = map(input, prompts)numeric_strings = filter(str.isnumeric, replies)numbers = map(float, numeric_strings)is_positive = (0.).__lt__valid_response = next(filter(is_positive, numbers))print(valid_response)
Enter a positive number: aI need a positive number! Try again: -5I need a positive number! Try again: 0I need a positive number! Try again: 55.0
Or, if you don't like that, you can always define your own function, or use the ones from the
operator
module.或者,如果您不喜欢这样,您可以随时定义自己的功能,或使用运算符模块中的功能。
-
Path existance:
Here one can usepathlib
library and itsPath.exists
method:路径存在:这里可以使用pathlib库及其Path.exists方法:
from itertools import chain, repeatfrom pathlib import Pathprompts = chain(["Enter a path: "], repeat("This path doesn't exist! Try again: "))replies = map(input, prompts)paths = map(Path, replies)valid_response = next(filter(Path.exists, paths))print(valid_response)
Enter a path: a b cThis path doesn't exist! Try again: 1This path doesn't exist! Try again: existing_file.txtexisting_file.txt
Limiting number of tries:
If you don't want to torture a user by asking him something an infinite number of times, you can specify a limit in a call of itertools.repeat
. This can be combined with providing a default value to the next
function:
如果你不想通过向他询问无数次来折磨用户,你可以在itertools.repeat的调用中指定一个限制。这可以与为下一个函数提供默认值相结合:
from itertools import chain, repeatprompts = chain(["Enter a number:"], repeat("Not a number! Try again:", 2))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies), None)print("You've failed miserably!" if valid_response is None else 'Well done!')
Enter a number: aNot a number! Try again: bNot a number! Try again: cYou've failed miserably!
Combining validation rules:
For a simple case, for example, when the program asks for age between 1 and 120, one can just add another filter
:
例如,对于一个简单的情况,当程序要求1到120之间的年龄时,可以添加另一个过滤器:
from itertools import chain, repeatprompt_msg = "Enter your age (1-120): "bad_input_msg = "Wrong input."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)numeric_replies = filter(str.isdigit, replies)ages = map(int, numeric_replies)positive_ages = filter((0).__lt__, ages)not_too_big_ages = filter((120).__ge__, positive_ages)valid_response = next(not_too_big_ages)print(valid_response)
But in the case when there are many rules, it's better to implement a function performing a logical conjunction. In the following example I will use a ready one from here:
但是在有许多规则的情况下,最好实现执行逻辑连接的函数。在下面的例子中,我将从这里使用一个现成的:
from functools import partialfrom itertools import chain, repeatfrom lz.logical import conjoindef is_one_letter(string: str) -> bool: return len(string) == 1rules = [str.isalpha, str.isupper, is_one_letter, 'C'.__le__, 'P'.__ge__]prompt_msg = "Enter a letter (C-P): "bad_input_msg = "Wrong input."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)valid_response = next(filter(conjoin(*rules), replies))print(valid_response)
Enter a letter (C-P): 5Wrong input.Enter a letter (C-P): fWrong input.Enter a letter (C-P): CDEWrong input.Enter a letter (C-P): QWrong input.Enter a letter (C-P): NN
Unfortunately, if someone needs a custom message for each failed case, then, I'm afraid, there is no pretty functional way. Or, at least, I couldn't find one.
不幸的是,如果某人需要为每个失败的案例提供自定义消息,那么,我担心,没有相当实用的方法。或者,至少,我找不到一个。
#13
Using Click:
Click is a library for command-line interfaces and it provides functionality for asking a valid response from a user.
Click是一个用于命令行界面的库,它提供了向用户请求有效响应的功能。
Simple example:
number = click.prompt('Please enter a number', type=float)print(number)
Please enter a number: aError: a is not a valid floating point valuePlease enter a number: 1010.0
Note how it converted the string value to a float automatically.
请注意它如何自动将字符串值转换为浮点数。
Checking if a value is within a range:
There are different custom types provided. To get a number in a specific range we can use IntRange
:
提供了不同的自定义类型。要获取特定范围内的数字,我们可以使用IntRange:
age = click.prompt("What's your age?", type=click.IntRange(1, 120))print(age)
What's your age?: aError: a is not a valid integerWhat's your age?: 0Error: 0 is not in the valid range of 1 to 120.What's your age?: 55
We can also specify just one of the limits, min
or max
:
我们还可以指定其中一个限制,最小值或最大值:
age = click.prompt("What's your age?", type=click.IntRange(min=14))print(age)
What's your age?: 0Error: 0 is smaller than the minimum valid value 14.What's your age?: 1818
Membership testing:
Using click.Choice
type. By default this check is case-sensitive.
使用click.Choice类型。默认情况下,此检查区分大小写。
choices = {'apple', 'orange', 'peach'}choice = click.prompt('Provide a fruit', type=click.Choice(choices, case_sensitive=False))print(choice)
Provide a fruit (apple, peach, orange): bananaError: invalid choice: banana. (choose from apple, peach, orange)Provide a fruit (apple, peach, orange): OrAnGeorange
Working with paths and files:
Using a click.Path
type we can check for existing paths and also resolve them:
使用click.Path类型,我们可以检查现有路径并解决它们:
path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))print(path)
Provide path: nonexistentError: Path "nonexistent" does not exist.Provide path: existing_folder'/path/to/existing_folder
Reading and writing files can be done by click.File
:
读取和写入文件可以通过click.File完成:
file = click.prompt('In which file to write data?', type=click.File('w'))with file.open(): file.write('Hello!')# More info about `lazy=True` at:# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safetyfile = click.prompt('Which file you wanna read?', type=click.File(lazy=True))with file.open(): print(file.read())
In which file to write data?: # <-- provided an empty string, which is an illegal name for a fileIn which file to write data?: some_file.txtWhich file you wanna read?: nonexistent.txtError: Could not open file: nonexistent.txt: No such file or directoryWhich file you wanna read?: some_file.txtHello!
Other examples:
Password confirmation:
password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)print(password)
Enter password: ······Repeat for confirmation: ·Error: the two entered values do not matchEnter password: ······Repeat for confirmation: ······qwerty
Default values:
In this case, simply pressing Enter (or whatever key you use) without entering a value, will give you a default one:
在这种情况下,只需按Enter键(或您使用的任何键)而不输入值,将为您提供一个默认值:
number = click.prompt('Please enter a number', type=int, default=42)print(number)
Please enter a number [42]: aError: a is not a valid integerPlease enter a number [42]: 42
#14
You can write more general logic to allow user to enter only specific number of times, as the same use-case arises in many real-world applications.
您可以编写更多通用逻辑,以允许用户仅输入特定次数,因为在许多实际应用程序中出现相同的用例。
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 iage = getValidInt()# do whatever you want to do.
#15
You can make the input statement a while True loop so it repeatedly asks for the users input and then break that loop if the user enters the response you would like. And you can use try and except blocks to handle invalid responses.
您可以将输入语句设置为True循环,以便它反复询问用户输入,然后在用户输入您想要的响应时中断该循环。并且您可以使用try和except块来处理无效响应。
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.")
The var variable is just so that if the user enters a string instead of a integer the program wont return "You are not able to vote in the United States."
var变量只是如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票”。
#16
Use "while" statement till user enter a true value and if the input value is not a number or it's a null value skip it and try to ask again and so on. In example I tried to answer truly your question. If we suppose that our age is between 1 and 150 then input value accepted, else it's a wrong value.For terminating program, the user can use 0 key and enter it as a value.
使用“while”语句直到用户输入一个真值,如果输入值不是数字或者它是一个空值,则跳过它并尝试再次询问,依此类推。在示例中,我试图回答真正的问题。如果我们假设我们的年龄在1到150之间,那么接受输入值,否则它是一个错误的值。对于终止程序,用户可以使用0键并将其作为值输入。
Note: Read comments top of code.
注意:阅读注释顶部的代码。
# 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.")
#17
Here's a cleaner, more generalized solution that avoids repetitive if/else blocks: write a function that takes (Error, error prompt) pairs in a dictionary and do all your value-checking with assertions.
这是一个更清晰,更通用的解决方案,可避免重复的if / else块:编写一个在字典中采用(错误,错误提示)对的函数,并使用断言进行所有值检查。
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)])
Usage:
d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', KeyboardInterrupt: 'You can never leave'}user_input = validate_input("Positive number: ", d)
#1
The simplest way to accomplish this would be to put the input
method in a while loop. Use continue
when you get bad input, and break
out of the loop when you're satisfied.
实现此目的的最简单方法是将输入方法放在while循环中。当输入错误时使用continue,当你满意时跳出循环。
When Your Input Might Raise an Exception
Use try and catch to detect when the user enters data that can't be parsed.
使用try和catch检测用户何时输入无法解析的数据。
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. breakif age >= 18: print("You are able to vote in the United States!")else: print("You are not able to vote in the United States.")
Implementing Your Own Validation Rules
If you want to reject values that Python can successfully parse, you can add your own validation logic.
如果要拒绝Python可以成功解析的值,可以添加自己的验证逻辑。
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. breakwhile 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
Combining Exception Handling and Custom Validation
Both of the above techniques can be combined into one loop.
上述两种技术都可以组合成一个循环。
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. breakif age >= 18: print("You are able to vote in the United States!")else: print("You are not able to vote in the United States.")
Encapsulating it All in a Function
If you need to ask your user for a lot of different values, it might be useful to put this code in a function, so you don't have to retype it every time.
如果您需要向用户询问许多不同的值,将此代码放在函数中可能很有用,因此您不必每次都重新键入它。
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 valueage = 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: ")
Putting It All Together
You can extend this idea to make a very generic input function:
您可以扩展这个想法,以创建一个非常通用的输入函数:
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
With usage such as:
使用如下:
age = sanitised_input("Enter your age: ", int, 1, 101)answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))
Common Pitfalls, and Why you Should Avoid Them
The Redundant Use of Redundant input
Statements
This method works but is generally considered poor style:
这种方法有效但通常被认为是不好的风格:
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): ")
It might look attractive initially because it's shorter than the while True
method, but it violates the Don't Repeat Yourself principle of software development. This increases the likelihood of bugs in your system. What if you want to backport to 2.7 by changing input
to raw_input
, but accidentally change only the first input
above? It's a SyntaxError
just waiting to happen.
它最初看起来很有吸引力,因为它比True方法短,但它违反了不要重复自己的软件开发原则。这会增加系统中出现错误的可能性。如果您想通过将输入更改为raw_input来向后移植到2.7,但是只是意外地更改了上面的第一个输入,该怎么办?这是一个等待发生的SyntaxError。
Recursion Will Blow Your Stack
If you've just learned about recursion, you might be tempted to use it in get_non_negative_int
so you can dispose of the while loop.
如果你刚刚学习了递归,你可能会想在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
This appears to work fine most of the time, but if the user enters invalid data enough times, the script will terminate with a RuntimeError: maximum recursion depth exceeded
. You may think "no fool would make 1000 mistakes in a row", but you're underestimating the ingenuity of fools!
这似乎在大多数情况下都能正常工作,但如果用户输入的数据足够多次,脚本将以RuntimeError终止:超出最大递归深度。你可能会认为“没有傻瓜会连续犯1000个错误”,但你低估了傻瓜的聪明才智!
#2
Why would you do a while True
and then break out of this loop while you can also just put your requirements in the while statement since all you want is to stop once you have the age?
你为什么要做一段时间才真正然后突破这个循环,你也可以把你的要求放在while语句中,因为你想要的就是在你有年龄后停止?
age = Nonewhile 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.")
This would result in the following:
这将导致以下结果:
Please enter your age: *potato*potato is not a number, please enter a number onlyPlease enter your age: *5*You are not able to vote in the United States.
this will work since age will never have a value that will not make sense and the code follows the logic of your "business process"
这将有效,因为年龄永远不会有一个没有意义的价值,代码遵循你的“业务流程”的逻辑
#3
Though the accepted answer is amazing. I would also like to share a quick hack for this problem. (This takes care of the negative age problem as well.)
虽然接受的答案是惊人的。我还想分享这个问题的快速入侵。 (这也解决了负面年龄问题。)
f=lambda age: (age.isdigit() and ((int(age)>=18 and "Can vote" ) or "Cannot vote")) or \f(input("invalid input. Try again\nPlease enter your age: "))print(f(input("Please enter your age: ")))
P.S. This code is for python 3.x.
附:此代码适用于python 3.x.
#4
So, I was messing around with something similar to this recently, and I came up with the following solution, which uses a way of getting input that rejects junk, before it's even checked in any logical way.
所以,我最近搞乱了类似于此的东西,我想出了以下解决方案,它使用一种获取输入的方法来拒绝垃圾,甚至在以任何逻辑方式检查之前。
read_single_keypress()
courtesy https://*.com/a/6599441/4532996
read_single_keypress()礼貌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 retdef 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 ydef _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()
You can find the complete module here.
你可以在这里找到完整的模块。
Example:
$ ./input_constrain.pycan you vote? age : asorry, age can only consist of digits.$ ./input_constrain.py can you vote? age : 23<RETURN>your age is 23You can vote!$ _
Note that the nature of this implementation is it closes stdin as soon as something that isn't a digit is read. I didn't hit enter after a
, but I needed to after the numbers.
请注意,此实现的性质是,只要读取非数字的内容,它就会关闭stdin。我之后没有按进入,但我需要在数字之后。
You could merge this with the thismany()
function in the same module to only allow, say, three digits.
您可以将它与同一模块中的thismany()函数合并为仅允许三位数。
#5
def validate_age(age): if age >=0 : return True return Falsewhile True: try: age = int(raw_input("Please enter your age:")) if validate_age(age): break except ValueError: print "Error: Invalid age."
#6
Building upon Daniel Q's and Patrick Artner's excellent suggestions,here is an even more generalized solution.
基于Daniel Q和Patrick Artner的出色建议,这是一个更加通用的解决方案。
# Assuming Python3import sysclass ValidationError(ValueError): # thanks Patrick Artner passdef 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)
I opted for explicit if
and raise
statements instead of an assert
,because assertion checking may be turned off,whereas validation should always be on to provide robustness.
我选择显式if和raise语句而不是断言,因为断言检查可能会被关闭,而验证应始终打开以提供稳健性。
This may be used to get different kinds of input,with different validation conditions.For example:
这可用于获得不同类型的输入,具有不同的验证条件。例如:
# 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!"})# Get a float in [0, 100]:percentage = validate_input("Percentage? ", cast=float, cond=lambda x: 0.0<=x<=100.0, onerror={ValidationError: "Must be between 0 and 100!", ValueError: "Not a number!"})
Or, to answer the original question:
或者,回答原始问题:
age = validate_input("Please enter your age: ", cast=int, cond=lambda a:0<=a<150, onerror={ValidationError: "Enter a plausible age, please!", ValueError: "Enter an integer, please!"})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.")
#7
Try this one:-
试试这个: -
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')
#8
While a try
/except
block will work, a much faster and cleaner way to accomplish this task would be to use str.isdigit()
.
虽然try / except块可以工作,但完成此任务的更快更简洁的方法是使用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.")
#9
One more solution for using input validation using a customized ValidationError
and a (optional) range validation for integer inputs:
使用定制ValidationError和(可选)范围验证对整数输入使用输入验证的另一种解决方案:
class ValidationError(ValueError): """Special validation error - its message is supposed to be printed""" passdef 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))
Usage:
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)
Output:
Pleased enter column: 22Columns must be in the range of 0 to 3 (inclusive)Pleased enter column: -2Columns must be in the range of 0 to 3 (inclusive)Pleased enter column: 2Pleased enter row: aInvalid input: aPleased enter row: 72Rows must be in the range of 5 to 15(exclusive)Pleased enter row: 9 9, 2
#10
Good question! You can try the following code for this. =)
好问题!您可以尝试以下代码。 =)
This code uses ast.literal_eval() to find the data type of the input (age
). Then it follows the following algorithm:
此代码使用ast.literal_eval()来查找输入的数据类型(年龄)。然后它遵循以下算法:
Ask user to input her/his
age
.要求用户输入她/他的年龄。
1.1. If
age
isfloat
orint
data type:1.1。如果age是float或int数据类型:
Check if
age>=18
. Ifage>=18
, print appropriate output and exit.检查年龄> = 18。如果年龄> = 18,打印适当的输出并退出。
Check if
0<age<18
. If0<age<18
, print appropriate output and exit.检查0 <年龄<18岁。如果0
If
age<=0
, ask the user to input a valid number for age again, (i.e. go back to step 1.)如果年龄<= 0,请要求用户再次输入年龄的有效数字(即返回步骤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.)1.2。如果age不是float或int数据类型,则要求用户再次输入她/他的年龄(即返回步骤1)。
Here is the code.
这是代码。
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 strflag = Truewhile(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.")
#11
Persistent user input using recursive function:
使用递归函数的持久用户输入:
String
def askName(): return input("Write your name: ").strip() or askName()name = askName()
Integer
def askAge(): try: return int(input("Enter your age: ")) except ValueError: return askAge()age = askAge()
and finally, the question requirement:
最后,问题要求:
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)
#12
Functional approach or "look mum no loops!":
from itertools import chain, repeatprompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies))print(valid_response)
Enter a number: aNot a number! Try again: bNot a number! Try again: 11
or if you want to have a "bad input" message separated from an input prompt as in other answers:
或者如果您希望将“错误输入”消息与输入提示分开,如在其他答案中那样:
prompt_msg = "Enter a number: "bad_input_msg = "Sorry, I didn't understand that."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies))print(valid_response)
Enter a number: aSorry, I didn't understand that.Enter a number: bSorry, I didn't understand that.Enter a number: 11
How does it work?
-
This combination ofprompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
itertools.chain
anditertools.repeat
will create an iterator which will yield strings"Enter a number: "
once, and"Not a number! Try again: "
an infinite number of times:for prompt in prompts: print(prompt)
Enter a number: Not a number! Try again: Not a number! Try again: Not a number! Try again: # ... and so on
-
replies = map(input, prompts)
- heremap
will apply all theprompts
strings from the previous step to theinput
function. E.g.:for reply in replies: print(reply)
Enter a number: aaNot a number! Try again: 11Not a number! Try again: it doesn't care nowit doesn't care now# and so on...
- We use
filter
andstr.isdigit
to filter out those strings that contain only digits:only_digits = filter(str.isdigit, replies)for reply in only_digits: print(reply)
And to get only the first digits-only string we useEnter a number: aNot a number! Try again: 11Not a number! Try again: 22Not a number! Try again: bNot a number! Try again: # and so on...
next
.
prompts = chain([“输入数字:”],重复(“不是数字!再试一次:”))itertools.chain和itertools.repeat的这个组合将创建一个迭代器,它将产生字符串“输入数字:”一次,和“不是数字!再试一次:”无数次:提示提示:打印(提示)输入数字:不是数字!再试一次:不是数字!再试一次:不是数字!再试一次:#...依此类推
replies = map(输入,提示) - 这里的地图将把上一步的所有提示字符串应用到输入函数。例如:回复中的回复:print(回复)输入一个数字:aaNot a number!再试一次:11没有数字!再试一次:它不关心现在不关心#等等......
我们使用filter和str.isdigit过滤掉那些只包含数字的字符串:only_digits = filter(str.isdigit,reply)for only in again_digits:print(reply)输入一个数字:a不是数字!再试一次:11没有数字!再试一次:22没有数字!再试一次:b不要一个号码!再试一次:#等等......并且只获取我们下一步使用的第一个数字字符串。
Other validation rules:
-
String methods: Of course you can use other string methods like
str.isalpha
to get only alphabetic strings, orstr.isupper
to get only uppercase. See docs for the full list.字符串方法:当然,您可以使用其他字符串方法(如str.isalpha)仅获取字母字符串,或者使用str.isupper仅获取大写字母。有关完整列表,请参阅文档。
-
Membership testing:
There are several different ways to perform it. One of them is by using__contains__
method:成员资格测试:有几种不同的方法可以执行它。其中一个是使用__contains__方法:
from itertools import chain, repeatfruits = {'apple', 'orange', 'peach'}prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))replies = map(input, prompts)valid_response = next(filter(fruits.__contains__, replies))print(valid_response)
Enter a fruit: 1I don't know this one! Try again: fooI don't know this one! Try again: appleapple
-
Numbers comparison:
There are useful comparison methods which we can use here. For example, for__lt__
(<
):数字比较:我们可以在这里使用有用的比较方法。例如,对于__lt __(<):
from itertools import chain, repeatprompts = chain(["Enter a positive number:"], repeat("I need a positive number! Try again:"))replies = map(input, prompts)numeric_strings = filter(str.isnumeric, replies)numbers = map(float, numeric_strings)is_positive = (0.).__lt__valid_response = next(filter(is_positive, numbers))print(valid_response)
Enter a positive number: aI need a positive number! Try again: -5I need a positive number! Try again: 0I need a positive number! Try again: 55.0
Or, if you don't like that, you can always define your own function, or use the ones from the
operator
module.或者,如果您不喜欢这样,您可以随时定义自己的功能,或使用运算符模块中的功能。
-
Path existance:
Here one can usepathlib
library and itsPath.exists
method:路径存在:这里可以使用pathlib库及其Path.exists方法:
from itertools import chain, repeatfrom pathlib import Pathprompts = chain(["Enter a path: "], repeat("This path doesn't exist! Try again: "))replies = map(input, prompts)paths = map(Path, replies)valid_response = next(filter(Path.exists, paths))print(valid_response)
Enter a path: a b cThis path doesn't exist! Try again: 1This path doesn't exist! Try again: existing_file.txtexisting_file.txt
Limiting number of tries:
If you don't want to torture a user by asking him something an infinite number of times, you can specify a limit in a call of itertools.repeat
. This can be combined with providing a default value to the next
function:
如果你不想通过向他询问无数次来折磨用户,你可以在itertools.repeat的调用中指定一个限制。这可以与为下一个函数提供默认值相结合:
from itertools import chain, repeatprompts = chain(["Enter a number:"], repeat("Not a number! Try again:", 2))replies = map(input, prompts)valid_response = next(filter(str.isdigit, replies), None)print("You've failed miserably!" if valid_response is None else 'Well done!')
Enter a number: aNot a number! Try again: bNot a number! Try again: cYou've failed miserably!
Combining validation rules:
For a simple case, for example, when the program asks for age between 1 and 120, one can just add another filter
:
例如,对于一个简单的情况,当程序要求1到120之间的年龄时,可以添加另一个过滤器:
from itertools import chain, repeatprompt_msg = "Enter your age (1-120): "bad_input_msg = "Wrong input."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)numeric_replies = filter(str.isdigit, replies)ages = map(int, numeric_replies)positive_ages = filter((0).__lt__, ages)not_too_big_ages = filter((120).__ge__, positive_ages)valid_response = next(not_too_big_ages)print(valid_response)
But in the case when there are many rules, it's better to implement a function performing a logical conjunction. In the following example I will use a ready one from here:
但是在有许多规则的情况下,最好实现执行逻辑连接的函数。在下面的例子中,我将从这里使用一个现成的:
from functools import partialfrom itertools import chain, repeatfrom lz.logical import conjoindef is_one_letter(string: str) -> bool: return len(string) == 1rules = [str.isalpha, str.isupper, is_one_letter, 'C'.__le__, 'P'.__ge__]prompt_msg = "Enter a letter (C-P): "bad_input_msg = "Wrong input."prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))replies = map(input, prompts)valid_response = next(filter(conjoin(*rules), replies))print(valid_response)
Enter a letter (C-P): 5Wrong input.Enter a letter (C-P): fWrong input.Enter a letter (C-P): CDEWrong input.Enter a letter (C-P): QWrong input.Enter a letter (C-P): NN
Unfortunately, if someone needs a custom message for each failed case, then, I'm afraid, there is no pretty functional way. Or, at least, I couldn't find one.
不幸的是,如果某人需要为每个失败的案例提供自定义消息,那么,我担心,没有相当实用的方法。或者,至少,我找不到一个。
#13
Using Click:
Click is a library for command-line interfaces and it provides functionality for asking a valid response from a user.
Click是一个用于命令行界面的库,它提供了向用户请求有效响应的功能。
Simple example:
number = click.prompt('Please enter a number', type=float)print(number)
Please enter a number: aError: a is not a valid floating point valuePlease enter a number: 1010.0
Note how it converted the string value to a float automatically.
请注意它如何自动将字符串值转换为浮点数。
Checking if a value is within a range:
There are different custom types provided. To get a number in a specific range we can use IntRange
:
提供了不同的自定义类型。要获取特定范围内的数字,我们可以使用IntRange:
age = click.prompt("What's your age?", type=click.IntRange(1, 120))print(age)
What's your age?: aError: a is not a valid integerWhat's your age?: 0Error: 0 is not in the valid range of 1 to 120.What's your age?: 55
We can also specify just one of the limits, min
or max
:
我们还可以指定其中一个限制,最小值或最大值:
age = click.prompt("What's your age?", type=click.IntRange(min=14))print(age)
What's your age?: 0Error: 0 is smaller than the minimum valid value 14.What's your age?: 1818
Membership testing:
Using click.Choice
type. By default this check is case-sensitive.
使用click.Choice类型。默认情况下,此检查区分大小写。
choices = {'apple', 'orange', 'peach'}choice = click.prompt('Provide a fruit', type=click.Choice(choices, case_sensitive=False))print(choice)
Provide a fruit (apple, peach, orange): bananaError: invalid choice: banana. (choose from apple, peach, orange)Provide a fruit (apple, peach, orange): OrAnGeorange
Working with paths and files:
Using a click.Path
type we can check for existing paths and also resolve them:
使用click.Path类型,我们可以检查现有路径并解决它们:
path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))print(path)
Provide path: nonexistentError: Path "nonexistent" does not exist.Provide path: existing_folder'/path/to/existing_folder
Reading and writing files can be done by click.File
:
读取和写入文件可以通过click.File完成:
file = click.prompt('In which file to write data?', type=click.File('w'))with file.open(): file.write('Hello!')# More info about `lazy=True` at:# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safetyfile = click.prompt('Which file you wanna read?', type=click.File(lazy=True))with file.open(): print(file.read())
In which file to write data?: # <-- provided an empty string, which is an illegal name for a fileIn which file to write data?: some_file.txtWhich file you wanna read?: nonexistent.txtError: Could not open file: nonexistent.txt: No such file or directoryWhich file you wanna read?: some_file.txtHello!
Other examples:
Password confirmation:
password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)print(password)
Enter password: ······Repeat for confirmation: ·Error: the two entered values do not matchEnter password: ······Repeat for confirmation: ······qwerty
Default values:
In this case, simply pressing Enter (or whatever key you use) without entering a value, will give you a default one:
在这种情况下,只需按Enter键(或您使用的任何键)而不输入值,将为您提供一个默认值:
number = click.prompt('Please enter a number', type=int, default=42)print(number)
Please enter a number [42]: aError: a is not a valid integerPlease enter a number [42]: 42
#14
You can write more general logic to allow user to enter only specific number of times, as the same use-case arises in many real-world applications.
您可以编写更多通用逻辑,以允许用户仅输入特定次数,因为在许多实际应用程序中出现相同的用例。
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 iage = getValidInt()# do whatever you want to do.
#15
You can make the input statement a while True loop so it repeatedly asks for the users input and then break that loop if the user enters the response you would like. And you can use try and except blocks to handle invalid responses.
您可以将输入语句设置为True循环,以便它反复询问用户输入,然后在用户输入您想要的响应时中断该循环。并且您可以使用try和except块来处理无效响应。
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.")
The var variable is just so that if the user enters a string instead of a integer the program wont return "You are not able to vote in the United States."
var变量只是如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票”。
#16
Use "while" statement till user enter a true value and if the input value is not a number or it's a null value skip it and try to ask again and so on. In example I tried to answer truly your question. If we suppose that our age is between 1 and 150 then input value accepted, else it's a wrong value.For terminating program, the user can use 0 key and enter it as a value.
使用“while”语句直到用户输入一个真值,如果输入值不是数字或者它是一个空值,则跳过它并尝试再次询问,依此类推。在示例中,我试图回答真正的问题。如果我们假设我们的年龄在1到150之间,那么接受输入值,否则它是一个错误的值。对于终止程序,用户可以使用0键并将其作为值输入。
Note: Read comments top of code.
注意:阅读注释顶部的代码。
# 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.")
#17
Here's a cleaner, more generalized solution that avoids repetitive if/else blocks: write a function that takes (Error, error prompt) pairs in a dictionary and do all your value-checking with assertions.
这是一个更清晰,更通用的解决方案,可避免重复的if / else块:编写一个在字典中采用(错误,错误提示)对的函数,并使用断言进行所有值检查。
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)])
Usage:
d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', KeyboardInterrupt: 'You can never leave'}user_input = validate_input("Positive number: ", d)