询问用户输入,直到他们给出有效的响应

时间:2021-01-12 18:21:48

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()来查找输入的数据类型(年龄)。然后它遵循以下算法:

  1. Ask user to input her/his age.

    要求用户输入她/他的年龄。

    1.1. If age is float or int data type:

    1.1。如果age是float或int数据类型:

    • Check if age>=18. If age>=18, print appropriate output and exit.

      检查年龄> = 18。如果年龄> = 18,打印适当的输出并退出。

    • Check if 0<age<18. If 0<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 not float or int 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?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    This combination of itertools.chain and itertools.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
  2. prompts = chain([“输入数字:”],重复(“不是数字!再试一次:”))itertools.chain和itertools.repeat的这个组合将创建一个迭代器,它将产生字符串“输入数字:”一次,和“不是数字!再试一次:”无数次:提示提示:打印(提示)输入数字:不是数字!再试一次:不是数字!再试一次:不是数字!再试一次:#...依此类推

  3. replies = map(input, prompts) - here map will apply all the prompts strings from the previous step to the input 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...
  4. replies = map(输入,提示) - 这里的地图将把上一步的所有提示字符串应用到输入函数。例如:回复中的回复:print(回复)输入一个数字:aaNot a number!再试一次:11没有数字!再试一次:它不关心现在不关心#等等......

  5. We use filter and str.isdigit to filter out those strings that contain only digits:
    only_digits = filter(str.isdigit, replies)for reply in only_digits:    print(reply)
    Enter 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...
    And to get only the first digits-only string we use next.
  6. 我们使用filter和str.isdigit过滤掉那些只包含数字的字符串:only_digits = filter(str.isdigit,reply)for only in again_digits:print(reply)输入一个数字:a不是数字!再试一次:11没有数字!再试一次:22没有数字!再试一次:b不要一个号码!再试一次:#等等......并且只获取我们下一步使用的第一个数字字符串。

Other validation rules:

  1. String methods: Of course you can use other string methods like str.isalpha to get only alphabetic strings, or str.isupper to get only uppercase. See docs for the full list.

    字符串方法:当然,您可以使用其他字符串方法(如str.isalpha)仅获取字母字符串,或者使用str.isupper仅获取大写字母。有关完整列表,请参阅文档。

  2. 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
  3. 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.

    或者,如果您不喜欢这样,您可以随时定义自己的功能,或使用运算符模块中的功能。

  4. Path existance:
    Here one can use pathlib library and its Path.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()来查找输入的数据类型(年龄)。然后它遵循以下算法:

  1. Ask user to input her/his age.

    要求用户输入她/他的年龄。

    1.1. If age is float or int data type:

    1.1。如果age是float或int数据类型:

    • Check if age>=18. If age>=18, print appropriate output and exit.

      检查年龄> = 18。如果年龄> = 18,打印适当的输出并退出。

    • Check if 0<age<18. If 0<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 not float or int 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?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    This combination of itertools.chain and itertools.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
  2. prompts = chain([“输入数字:”],重复(“不是数字!再试一次:”))itertools.chain和itertools.repeat的这个组合将创建一个迭代器,它将产生字符串“输入数字:”一次,和“不是数字!再试一次:”无数次:提示提示:打印(提示)输入数字:不是数字!再试一次:不是数字!再试一次:不是数字!再试一次:#...依此类推

  3. replies = map(input, prompts) - here map will apply all the prompts strings from the previous step to the input 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...
  4. replies = map(输入,提示) - 这里的地图将把上一步的所有提示字符串应用到输入函数。例如:回复中的回复:print(回复)输入一个数字:aaNot a number!再试一次:11没有数字!再试一次:它不关心现在不关心#等等......

  5. We use filter and str.isdigit to filter out those strings that contain only digits:
    only_digits = filter(str.isdigit, replies)for reply in only_digits:    print(reply)
    Enter 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...
    And to get only the first digits-only string we use next.
  6. 我们使用filter和str.isdigit过滤掉那些只包含数字的字符串:only_digits = filter(str.isdigit,reply)for only in again_digits:print(reply)输入一个数字:a不是数字!再试一次:11没有数字!再试一次:22没有数字!再试一次:b不要一个号码!再试一次:#等等......并且只获取我们下一步使用的第一个数字字符串。

Other validation rules:

  1. String methods: Of course you can use other string methods like str.isalpha to get only alphabetic strings, or str.isupper to get only uppercase. See docs for the full list.

    字符串方法:当然,您可以使用其他字符串方法(如str.isalpha)仅获取字母字符串,或者使用str.isupper仅获取大写字母。有关完整列表,请参阅文档。

  2. 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
  3. 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.

    或者,如果您不喜欢这样,您可以随时定义自己的功能,或使用运算符模块中的功能。

  4. Path existance:
    Here one can use pathlib library and its Path.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)