关于python:请求用户输入,直到他们给出有效的响应

Asking the user for input until they give a valid response

我正在编写一个必须接受用户输入的程序。

1
2
3
4
5
6
#note: Python 2.7 users should use `raw_input`, the equivalent of 3.X's `input`
age = int(input("Please enter your age:"))
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

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

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

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

1
2
3
4
5
6
C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Traceback (most recent call last):
  File"canyouvote.py", line 1, in <module>
    age = int(input("Please enter your age:"))
ValueError: invalid literal for int() with base 10: 'dickety six'

我希望它能再次尝试获取输入,而不是崩溃。这样地:

1
2
3
4
5
C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Sorry, I didn't understand that.
Please enter your age: 26
You are able to vote in the United States!

我怎样才能做到这一点?如果我还想拒绝像-1这样的值,这是一个有效的int,但在这个上下文中是荒谬的呢?


实现这一点的最简单方法是将input方法放入while循环中。当你得到错误的输入时使用continue,当你满意时使用break退出循环。

当您的输入可能引发异常时

使用Try and Catch检测用户何时输入无法分析的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while True:
    try:
        # Note: Python 2.x users should use raw_input, the equivalent of 3.x's input
        age = int(input("Please enter your age:"))
    except ValueError:
        print("Sorry, I didn't understand that.")
        #better try again... Return to the start of the loop
        continue
    else:
        #age was successfully parsed!
        #we're ready to exit the loop.
        break
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

实现您自己的验证规则

如果要拒绝Python可以成功解析的值,可以添加自己的验证逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while True:
    data = input("Please enter a loud message (must be all caps):")
    if not data.isupper():
        print("Sorry, your response was not loud enough.")
        continue
    else:
        #we're happy with the value given.
        #we're ready to exit the loop.
        break

while True:
    data = input("Pick an answer from A to D:")
    if data.lower() not in ('a', 'b', 'c', 'd'):
        print("Not an appropriate choice.")
    else:
        break

结合异常处理和自定义验证

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
while True:
    try:
        age = int(input("Please enter your age:"))
    except ValueError:
        print("Sorry, I didn't understand that.")
        continue

    if age < 0:
        print("Sorry, your response must not be negative.")
        continue
    else:
        #age was successfully parsed, and we're happy with its value.
        #we're ready to exit the loop.
        break
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

将它全部封装在一个函数中

如果您需要向用户询问许多不同的值,将此代码放入函数中可能会很有用,因此您不必每次都重新键入它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def get_non_negative_int(prompt):
    while True:
        try:
            value = int(input(prompt))
        except ValueError:
            print("Sorry, I didn't understand that.")
            continue

        if value < 0:
            print("Sorry, your response must not be negative.")
            continue
        else:
            break
    return value

age = get_non_negative_int("Please enter your age:")
kids = get_non_negative_int("Please enter the number of children you have:")
salary = get_non_negative_int("Please enter your yearly earnings, in dollars:")

把它们放在一起

您可以扩展此思想,以生成非常通用的输入函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
    if min_ is not None and max_ is not None and max_ < min_:
        raise ValueError("min_ must be less than or equal to max_.")
    while True:
        ui = input(prompt)
        if type_ is not None:
            try:
                ui = type_(ui)
            except ValueError:
                print("Input type must be {0}.".format(type_.__name__))
                continue
        if max_ is not None and ui > max_:
            print("Input must be less than or equal to {0}.".format(max_))
        elif min_ is not None and ui < min_:
            print("Input must be greater than or equal to {0}.".format(min_))
        elif range_ is not None and ui not in range_:
            if isinstance(range_, range):
                template ="Input must be between {0.start} and {0.stop}."
                print(template.format(range_))
            else:
                template ="Input must be {0}."
                if len(range_) == 1:
                    print(template.format(*range_))
                else:
                    print(template.format(" or".join((",".join(map(str,
                                                                     range_[:-1])),
                                                       str(range_[-1])))))
        else:
            return ui

使用方法如下:

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

常见的陷阱,以及为什么要避免它们冗余使用input语句

这种方法有效,但通常被认为是劣质的:

1
2
3
4
data = input("Please enter a loud message (must be all caps):")
while not data.isupper():
    print("Sorry, your response was not loud enough.")
    data = input("Please enter a loud message (must be all caps):")

最初它可能看起来很有吸引力,因为它比while True方法短,但它违反了软件开发的"不要重复自己"原则。这会增加系统中出现错误的可能性。如果您想通过将input更改为raw_input,而不小心只更改上面的第一个input,从而返回到2.7,该怎么办?这是一个等待发生的SyntaxError

递归将破坏您的堆栈

如果您刚刚了解了递归,那么您可能会尝试在get_non_negative_int中使用它,以便可以处理while循环。

1
2
3
4
5
6
7
8
9
10
11
12
def get_non_negative_int(prompt):
    try:
        value = int(input(prompt))
    except ValueError:
        print("Sorry, I didn't understand that.")
        return get_non_negative_int(prompt)

    if value < 0:
        print("Sorry, your response must not be negative.")
        return get_non_negative_int(prompt)
    else:
        return value

这在大多数情况下似乎可以正常工作,但是如果用户输入无效数据的次数足够多,脚本将以RuntimeError: maximum recursion depth exceeded终止。你可能认为"没有哪个傻瓜会连续犯1000个错误",但你低估了傻瓜的创造力!


为什么你要做一个while True,然后跳出这个循环,同时你也可以把你的需求放在while语句中,因为你想要的只是一旦你有了年龄就停止?

1
2
3
4
5
6
7
8
9
10
11
12
13
age = None
while age is None:
    input_value = input("Please enter your age:")
    try:
        # try and convert the string input to a number
        age = int(input_value)
    except ValueError:
        # tell the user off
        print("{input} is not a number, please enter a number only".format(input=input_value))
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

这将导致以下结果:

1
2
3
4
Please enter your age: *potato*
potato is not a number, please enter a number only
Please enter your age: *5*
You are not able to vote in the United States.

这是可行的,因为年龄永远不会有一个没有意义的价值,代码遵循"业务流程"的逻辑。


虽然公认的答案是惊人的。我还想分享这个问题的快速黑客。(这也解决了消极的年龄问题。)

1
2
3
4
f=lambda age: (age.isdigit() and ((int(age)>=18  and"Can vote" ) or"Cannot vote")) or \
f(input("invalid input. Try again
Please enter your age:"
))
print(f(input("Please enter your age:")))

另外,这个代码是针对python 3.x的。


所以,我最近在搞类似的事情,我想出了下面的解决方案,它使用一种方法来获取拒绝垃圾邮件的输入,甚至在以任何合乎逻辑的方式检查之前。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def read_single_keypress() -> str:
   """Waits for a single keypress on stdin.
    -- from :: https://stackoverflow.com/a/6599441/4532996
   """


    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    try:
        ret = sys.stdin.read(1) # returns a single character
    except KeyboardInterrupt:
        ret = 0
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return ret

def until_not_multi(chars) -> str:
   """read stdin until !(chars)"""
    import sys
    chars = list(chars)
    y =""
    sys.stdout.flush()
    while True:
        i = read_single_keypress()
        _ = sys.stdout.write(i)
        sys.stdout.flush()
        if i not in chars:
            break
        y += i
    return y

def _can_you_vote() -> str:
   """a practical example:
    test if a user can vote based purely on keypresses"""

    print("can you vote? age :", end="")
    x = int("0" + until_not_multi("0123456789"))
    if not x:
        print("
sorry, age can only consist of digits."
)
        return
    print("your age is", x,"
You can vote!"
if x >= 18 else"Sorry! you can't vote")

_can_you_vote()

您可以在这里找到完整的模块。

例子:

1
2
3
4
5
6
7
8
$ ./input_constrain.py
can you vote? age : a
sorry, age can only consist of digits.
$ ./input_constrain.py
can you vote? age : 23<RETURN>
your age is 23
You can vote!
$ _

请注意,此实现的本质是,一旦读取到非数字的内容,它就会关闭stdin。我没有在a之后按回车键,但我需要在数字之后按回车键。

您可以将它与同一模块中的thismany()函数合并,以仅允许(例如)三位数字。


要编辑代码并修复错误:

1
2
3
4
5
6
7
8
9
10
11
while True:
    try:
       age = int(input("Please enter your age:"))
       if age >= 18:
           print("You are able to vote in the United States!")
           break
       else:
           print("You are not able to vote in the United States.")
           break
    except ValueError:
       print("Please enter a valid response")

1
2
3
4
5
6
7
8
9
10
11
def validate_age(age):
    if age >=0 :
        return True
    return False

while True:
    try:
        age = int(raw_input("Please enter your age:"))
        if validate_age(age): break
    except ValueError:
        print"Error: Invalid age."


试试这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def takeInput(required):
  print 'ooo or OOO to exit'
  ans = raw_input('Enter: ')

  if not ans:
      print"You entered nothing...!"
      return takeInput(required)

      ##  FOR Exit  ##
  elif ans in ['ooo', 'OOO']:
    print"Closing instance."
    exit()

  else:
    if ans.isdigit():
      current = 'int'
    elif set('[~!@#$%^&*()_+{}":/\']+$').intersection(ans):
      current = 'other'
    elif isinstance(ans,basestring):
      current = 'str'        
    else:
      current = 'none'

  if required == current :
    return ans
  else:
    return takeInput(required)

## pass the value in which type you want [str/int/special character(as other )]
print"input:", takeInput('str')

使用递归函数的持久用户输入:

1
2
3
4
def askName():
    return input("Write your name:").strip() or askName()

name = askName()

整数

1
2
3
4
5
def askAge():
    try: return int(input("Enter your age:"))
    except ValueError: return askAge()

age = askAge()

最后,问题要求:

1
2
3
4
5
6
7
8
9
10
11
12
def askAge():
    try: return int(input("Enter your age:"))
    except ValueError: return askAge()

age = askAge()

responseAge = [
   "You are able to vote in the United States!",
   "You are not able to vote in the United States.",
][int(age < 18)]

print(responseAge)

基于丹尼尔·Q和帕特里克·阿特纳的优秀建议,这里有一个更广义的解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Assuming Python3
import sys

class ValidationError(ValueError):  # thanks Patrick Artner
    pass

def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None):
    if onerror==None: onerror = {}
    while True:
        try:
            data = cast(input(prompt))
            if not cond(data): raise ValidationError
            return data
        except tuple(onerror.keys()) as e:  # thanks Daniel Q
            print(onerror[type(e)], file=sys.stderr)

我选择了明确的ifraise声明,而不是assert,因为断言检查可能被关闭,然而,验证应该始终持续以提供健壮性。

这可用于获取不同类型的输入,验证条件不同。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# No validation, equivalent to simple input:
anystr = validate_input("Enter any string:")

# Get a string containing only letters:
letters = validate_input("Enter letters:",
    cond=str.isalpha,
    onerror={ValidationError:"Only letters, please!
<hr><P>您可以将输入语句设为while-true循环,以便它反复请求用户输入,然后在用户输入您想要的响应时中断该循环。您可以使用try和except块来处理无效的响应。</P>[cc lang="
python"]while True:

    var = True

    try:
        age = int(input("
Please enter your age:"))

    except ValueError:
        print("
Invalid input.")
        var = False

    if var == True:
        if age >= 18:
                print("
You are able to vote in the United States.")
                break
        else:
            print("
You are not able to vote in the United States.")

var变量的作用是,如果用户输入字符串而不是整数,程序将不会返回"您在美国无法投票"。


使用Try-Catch和Never-Ending While循环。若要检查空字符串,请使用if语句检查字符串是否为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
while True:
    name = input("Enter Your Name
"
)
    if not name:
        print("I did not understood that")
        continue
    else:
        break

while True:
    try:
        salary = float(input("whats ur salary
"
))
    except ValueError:
        print("I did not understood that")
        continue
    else:
        break

while True:
    try:
        print("whats ur age?")
        age = int(float(input()))
    except ValueError:
        print("I did not understood that")
        continue
    else:
        break

print("Hello"+ name + "
Your salary is"
+ str(salary) + '
and you will be '
+ str(age+1) +' in a Year')


这将继续要求用户输入数字,直到他们输入有效的数字:

1
2
3
4
5
6
7
8
9
10
11
12
#note: Python 2.7 users should use raw_input, the equivalent of 3.X's input
while(1):
    try:
        age = int(input("Please enter your age:"))
        if age >= 18:
            print("You are able to vote in the United States!")
            break()
        else:
            print("You are not able to vote in the United States.")
            break()
    except:
        print("Please only enter numbers")

您可以编写更通用的逻辑,允许用户只输入特定的次数,因为在许多实际应用程序中都会出现相同的用例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def getValidInt(iMaxAttemps = None):
  iCount = 0
  while True:
    # exit when maximum attempt limit has expired
    if iCount != None and iCount > iMaxAttemps:
       return 0     # return as default value

    i = raw_input("Enter no")
    try:
       i = int(i)
    except ValueError as e:
       print"Enter valid int value"
    else:
       break

    return i

age = getValidInt()
# do whatever you want to do.


使用自定义ValidationError和整数输入(可选)范围验证的输入验证的另一个解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ValidationError(ValueError):
   """Special validation error - its message is supposed to be printed"""
    pass

def RangeValidator(text,num,r):
   """Generic validator - raises 'text' as ValidationError if 'num' not in range 'r'."""
    if num in r:
        return num
    raise ValidationError(text)

def ValidCol(c):
   """Specialized column validator providing text and range."""
    return RangeValidator("Columns must be in the range of 0 to 3 (inclusive)",
                          c, range(4))

def ValidRow(r):
   """Specialized row validator providing text and range."""
    return RangeValidator("Rows must be in the range of 5 to 15(exclusive)",
                          r, range(5,15))

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def GetInt(text, validator=None):
   """Aks user for integer input until a valid integer is given. If provided,
    a 'validator' function takes the integer and either raises a
    ValidationError to be printed or returns the valid number.
    Non integers display a simple error message."""

    print()
    while True:
        n = input(text)
        try:
            n = int(n)

            return n if validator is None else validator(n)

        except ValueError as ve:
            # prints ValidationErrors directly - else generic message:
            if isinstance(ve, ValidationError):
                print(ve)
            else:
                print("Invalid input:", n)


column = GetInt("Pleased enter column:", ValidCol)
row = GetInt("Pleased enter row:", ValidRow)
print( row, column)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
Pleased enter column: 22
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: -2
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: 2
Pleased enter row: a
Invalid input:  a
Pleased enter row: 72
Rows must be in the range of 5 to 15(exclusive)
Pleased enter row: 9  

9, 2

虽然try/except块可以工作,但要完成这项任务,一种更快、更清洁的方法是使用str.isdigit()

1
2
3
4
5
6
7
8
9
10
11
12
while True:
    age = input("Please enter your age:")
    if age.isdigit():
        age = int(age)
        break
    else:
        print("Invalid number '{age}'. Try again.".format(age=age))

if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")


这里有一个更清晰、更通用的解决方案,可以避免重复的if/else块:编写一个函数,在字典中使用(错误、错误提示)对,并使用断言检查所有值。

1
2
3
4
5
6
7
8
9
10
11
def validate_input(prompt, error_map):
    while True:
        try:
            data = int(input(prompt))
            # Insert your non-exception-throwing conditionals here
            assert data > 0
            return data
        # Print whatever text you want the user to see
        # depending on how they messed up
        except tuple(error_map.keys()) as e:
            print(error_map[type(e)])

用途:

1
2
3
d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only',
     KeyboardInterrupt: 'You can never leave'}
user_input = validate_input("Positive number:", d)


Good question! You can try the following code for this. =)

This code uses ast.literal_eval() to find the data type of the input (age). Then it follows the following algorithm:

  • Ask user to input her/his age.

    1.1. If age is float or int data type:

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

    • Check if 0. If 0, print appropriate output and exit.

    • If age<=0, ask the user to input a valid number for age again, (i.e. go back to step 1.)

    1.2. If age is not float or int data type, then ask user to input her/his age again (i.e. go back to step 1.)

  • Here is the code.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from ast import literal_eval

    ''' This function is used to identify the data type of input data.'''
    def input_type(input_data):
        try:
            return type(literal_eval(input_data))
        except (ValueError, SyntaxError):
            return str

    flag = True

    while(flag):
        age = raw_input("Please enter your age:")

        if input_type(age)==float or input_type(age)==int:
            if eval(age)>=18:
                print("You are able to vote in the United States!")
                flag = False
            elif eval(age)>0 and eval(age)<18:
                print("You are not able to vote in the United States.")
                flag = False
            else: print("Please enter a valid number as your age.")

        else: print("Sorry, I didn't understand that.")

    使用"while"语句,直到用户输入一个真值,如果输入值不是数字或是空值,请跳过它,然后再次尝试询问,依此类推。举个例子,我试图真正回答你的问题。如果我们假设我们的年龄在1到150岁之间,那么输入值被接受,否则它是一个错误的值。对于终止程序,用户可以使用0键并将其作为值输入。

    Note: Read comments top of code.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # If your input value is only a number then use"Value.isdigit() == False".
    # If you need an input that is a text, you should remove"Value.isdigit() == False".
    def Input(Message):
        Value = None
        while Value == None or Value.isdigit() == False:
            try:        
                Value = str(input(Message)).strip()
            except InputError:
                Value = None
        return Value

    # Example:
    age = 0
    # If we suppose that our age is between 1 and 150 then input value accepted,
    # else it's a wrong value.
    while age <=0 or age >150:
        age = int(Input("Please enter your age:"))
        # For terminating program, the user can use 0 key and enter it as an a value.
        if age == 0:
            print("Terminating ...")
            exit(0)

    if age >= 18 and age <=150:
        print("You are able to vote in the United States!")
    else:
        print("You are not able to vote in the United States.")