请求用户输入,直至他们给出有效回答

751
我正在编写一个可以接受用户输入的程序。
#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.")

该程序只有在用户输入有意义的数据时才能正常工作。
Please enter your age: 23
You are able to vote in the United States!

但是,如果用户输入无效数据,则会失败:

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'

不要让程序崩溃,我希望它能再次要求输入。就像这样:

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

22个回答

987
最简单的方法是将input方法放在一个while循环中。当输入有误时,请使用continue,并在满意时使用break退出循环。

当您的输入可能引发异常

请使用tryexcept来检测用户输入无法解析的数据。
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可以成功解析的值,您可以添加自己的验证逻辑。

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

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

以上两种技术可以合并到一个循环中。

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

封装成函数

如果您需要向用户请求许多不同的值,将此代码放入函数中可能会很有用,这样您就不必每次重新输入它。

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

将所有内容整合

您可以扩展此想法,以创建一个非常通用的输入函数:

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:
                    expected = " or ".join((
                        ", ".join(str(x) for x in range_[:-1]),
                        str(range_[-1])
                    ))
                    print(template.format(expected))
        else:
            return ui

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

常见陷阱以及为何应避免它们

冗余使用冗余的input语句

这种方法虽然可行,但通常被认为是不良风格:

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

这种方法看起来一开始很吸引人,因为它比while True的方法要短,但它违反了软件开发的不要重复自己原则。这增加了系统中错误发生的可能性。如果你想通过将input更改为raw_input来回溯到2.7,但意外地只更改了上面的第一个input怎么办?这就是一个等着发生SyntaxError的事情。

递归会让你的堆栈溢出

如果你刚了解递归,你可能会试图在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

这似乎大部分时间都能正常工作,但如果用户多次输入无效数据,脚本将以“RuntimeError:maximum recursion depth exceeded”终止。你可能会认为“没有傻瓜会连续犯1000个错误”,但你低估了傻瓜的创造力!

85
阅读许多例子确实很有趣,谢谢。低估的教训是:“不要低估愚蠢者的机智!” - vpibano
5
我不仅会为这两个问答点赞,因为它们非常棒,而且你用“dickety six”一词巩固了这个决定。干得好,@Kevin。 - erekalper
2
不要低估愚蠢的人和聪明的攻击者的创造力。这种情况下,DOS攻击可能是最容易的,但也可能存在其他攻击方式。 - Solomon Ucko
1
@JArunMani 我不认为这会是差劲的风格,但可能会稍微难以阅读。你确实每个循环只有一个 input,循环会变得非常短,但条件可能会变得相当长... - Tomerikoo
5
@laundmo,当然,我将把我所写的代码块放入公共领域。您可以在任何情况下自由使用它们,无需获得我的明确许可或知晓。关于非代码块部分,如果您想将我的整个回答粘贴到您正在编写的“学习Python”书籍中,请我们谈一下版税的问题;-) - Kevin
显示剩余8条评论

62
为什么你要使用 while True 循环然后再退出,而你也可以直接在 while 语句中放置你的需求,因为你只是想在年龄符合条件时停止循环?
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.")

这将导致以下结果:

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.

由于年龄的值永远不会没有意义,而且代码遵循“业务流程”的逻辑,因此这将有效。


一个精心设计的__退出条件__,如此建议,避免了由于while True而导致无限循环的陷阱,而没有安全地到达break或return。 - hc_dev

41

函数式编程 或者 "不要循环,妈妈看看!":

from itertools import chain, repeat

prompts = 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:  a
Not a number! Try again:  b
Not a number! Try again:  1
1

如果你想要一个与输入提示分开的“错误输入”消息,就像其他答案中一样:

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:  a
Sorry, I didn't understand that.
Enter a number:  b
Sorry, I didn't understand that.
Enter a number:  1
1

它是如何工作的?

  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. 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:  a
    a
    Not a number! Try again:  1
    1
    Not a number! Try again:  it doesn't care now
    it doesn't care now
    # and so on...
    
  3. 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:  a
    Not a number! Try again:  1
    1
    Not a number! Try again:  2
    2
    Not a number! Try again:  b
    Not a number! Try again: # and so on...
    
    And to get only the first digits-only string we use next.

其他验证规则:

  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.

  2. Membership testing:
    There are several different ways to perform it. One of them is by using __contains__ method:

    from itertools import chain, repeat
    
    fruits = {'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:  1
    I don't know this one! Try again:  foo
    I don't know this one! Try again:  apple
    apple
    
  3. Numbers comparison:
    There are useful comparison methods which we can use here. For example, for __lt__ (<):

    from itertools import chain, repeat
    
    prompts = 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: a
    I need a positive number! Try again: -5
    I need a positive number! Try again: 0
    I need a positive number! Try again: 5
    5.0
    

    Or, if you don't like using dunder methods (dunder = double-underscore), 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:

    from itertools import chain, repeat
    from pathlib import Path
    
    prompts = 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 c
    This path doesn't exist! Try again:  1
    This path doesn't exist! Try again:  existing_file.txt
    existing_file.txt
    

限制尝试次数:

如果你不想让用户无限次地回答一个问题,你可以在调用itertools.repeat函数时指定一个限制次数。这可以与向 next 函数提供默认值相结合:

from itertools import chain, repeat

prompts = 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: a
Not a number! Try again: b
Not a number! Try again: c
You've failed miserably!

预处理输入数据:

有时,如果用户意外以大写字母或在字符串开头或结尾处添加空格,我们不希望拒绝输入。为了考虑这些简单的错误,我们可以通过应用str.lowerstr.strip方法来预处理输入数据。例如,对于成员资格测试的情况,代码将如下所示:

from itertools import chain, repeat

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
lowercased_replies = map(str.lower, replies)
stripped_replies = map(str.strip, lowercased_replies)
valid_response = next(filter(fruits.__contains__, stripped_replies))
print(valid_response)

Enter a fruit:  duck
I don't know this one! Try again:     Orange
orange

如果您有许多用于预处理的函数,使用执行函数组合的函数可能更容易。例如,可以使用此处的函数:

from itertools import chain, repeat

from lz.functional import compose

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower)  # you can add more functions here
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)

Enter a fruit:  potato
I don't know this one! Try again:   PEACH
peach

组合验证规则:

对于一个简单的情况,例如程序要求年龄在1到120之间,可以添加另一个filter来实现:

from itertools import chain, repeat

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

但是当有很多规则时,最好实现一个执行逻辑合取的函数。在下面的示例中,我将使用这里提供的一个现成的函数:

from functools import partial
from itertools import chain, repeat

from lz.logical import conjoin


def is_one_letter(string: str) -> bool:
    return len(string) == 1


rules = [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):  5
Wrong input.
Enter a letter (C-P):  f
Wrong input.
Enter a letter (C-P):  CDE
Wrong input.
Enter a letter (C-P):  Q
Wrong input.
Enter a letter (C-P):  N
N

很不幸,如果有人需要每个失败案例的自定义消息,那么恐怕就没有一个漂亮的功能方法了。或者至少我找不到。


2
多么彻底而精彩的回答,解释得非常清晰易懂。 - Locane
使用您的风格,如何去除空白并将输入转换为小写以进行成员测试?我不想创建一个必须包含大写和小写示例的集合。我还希望允许输入错误的空格。 - Austin
1
@Austin,我添加了一个新的预处理部分。请看一下。 - Georgy
那让我想起了 ReactiveX。但也许它最初受到函数式语言的启发? - Mateen Ulhaq
谢谢你的回答。我从来没有想过可以那样结合迭代器、映射和输入。真是让我大开眼界。使用lambda表达式和过滤器是否也会很好用呢? - Manny Fleurmond

29

虽然已有的答案非常好,但我也想分享一个快速解决这个问题的技巧。(这也解决了年龄为负数的问题。)

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

附言:此代码适用于Python 3.x。


1
请注意,此代码是递归的,但在这里并不需要递归,正如Kevin所说,它可能会导致堆栈溢出。 - PM 2Ring
3
@PM2Ring - 你说得对。但是我在这里的目的只是为了展示如何通过“短路”来最小化(美化)冗长的代码。 - aaveg
15
为什么要将lambda函数赋值给一个变量,直接使用def即可。def f(age):f = lambda age:更加清晰易懂。 - GP89
4
在某些情况下,你可能只需要一次使用年龄信息,之后该函数就无用了。有时候人们可能想使用一个函数,在完成任务后就将其丢弃。虽然这不一定是最好的方式,但它绝对是一种不同的解决方案(这也是我提供解决方案的目的)。 - aaveg
@aaveg,你怎么将这段代码修改以便实际保存用户提供的年龄? - Tytire Recubans
找到了,只需返回年龄:f=lambda age: (age.isdigit() and (int(age)>=18 and age)) or \ f(input("无效输入,请重试\n请输入您的年龄: ")) print((input("请输入您的年龄: "))) - Tytire Recubans

22

使用Click:

Click是用于命令行界面的库,它提供了从用户那里获取有效响应的功能。

简单示例:

import click

number = click.prompt('Please enter a number', type=float)
print(number)

Please enter a number: 
 a
Error: a is not a valid floating point value
Please enter a number: 
 10
10.0

注意它如何自动将字符串值转换为浮点数。

检查值是否在范围内:

提供了不同的自定义类型。要获取特定范围内的数字,我们可以使用 IntRange

age = click.prompt("What's your age?", type=click.IntRange(1, 120))
print(age)

What's your age?: 
 a
Error: a is not a valid integer
What's your age?: 
 0
Error: 0 is not in the valid range of 1 to 120.
What's your age?: 
 5
5

我们还可以仅指定一个限制:minmax:

age = click.prompt("What's your age?", type=click.IntRange(min=14))
print(age)

What's your age?: 
 0
Error: 0 is smaller than the minimum valid value 14.
What's your age?: 
 18
18

成员测试:

使用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): 
 banana
Error: invalid choice: banana. (choose from apple, peach, orange)
Provide a fruit (apple, peach, orange): 
 OrAnGe
orange

处理路径和文件:

使用click.Path类型,我们可以检查现有路径并解决它们:

path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))
print(path)

Provide path: 
 nonexistent
Error: Path "nonexistent" does not exist.
Provide path: 
 existing_folder
'/path/to/existing_folder

可以通过 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-safety
file = 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 file
In which file to write data?: 
 some_file.txt
Which file you wanna read?: 
 nonexistent.txt
Error: Could not open file: nonexistent.txt: No such file or directory
Which file you wanna read?: 
 some_file.txt
Hello!

其他例子:

密码确认:

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 match
Enter password: 
 ······
Repeat for confirmation: 
 ······
qwerty

默认值:

在这种情况下,仅按下Enter(或您使用的其他键)而不输入值将给你一个默认值:

number = click.prompt('Please enter a number', type=int, default=42)
print(number)

Please enter a number [42]: 
 a
Error: a is not a valid integer
Please enter a number [42]: 
 
42

谢谢,这是完美的。使用有效范围内的数字选择进行循环正是我想要的。 - Amir

20

我是Unix哲学“专注于一件事并把它做好”的忠实粉丝。捕捉用户输入和验证它们是两个不同的步骤:

  • 使用get_input提示用户输入,直到输入正确
  • 使用可以传递给get_inputvalidator函数进行验证

可以保持简单(Python 3.8+,使用海象运算符):

def get_input(
    prompt="Enter a value: ",
    validator=lambda x: True,
    error_message="Invalid input. Please try again.",
):
    while not validator(value := input(prompt)):
        print(error_message)
    return value

def is_positive_int(value):
    try:
        return int(value) >= 0
    except ValueError:
        return False

if __name__ == "__main__":
    val = get_input("Give a positive number: ", is_positive_int)
    print(f"OK, thanks for {val}")

演示运行:

Give a positive number: -5
Invalid input. Please try again.
Give a positive number: asdf
Invalid input. Please try again.
Give a positive number:
Invalid input. Please try again.
Give a positive number: 42
OK, thanks for 42

在 Python < 3.8 中,您可以像这样使用 get_input

def get_input(
    prompt="Enter a value: ",
    validator=lambda x: True,
    error_message="Invalid input. Please try again.",
):
    while True:
        value = input(prompt)
        if validator(value):
            return value
        print(error_message)

在终止应用程序之前,您也可以处理 KeyboardInterrupt 并打印友好的退出消息。如果需要,可以使用计数器来限制允许的重试次数。


15

最近我在尝试类似的东西,然后想出了以下解决方案,使用一种拒绝垃圾输入的输入方式,在逻辑上检查之前就将其拒绝。

read_single_keypress() 来自 https://dev59.com/XXNA5IYBdhLWcg3wYMx-#6599441

def read_single_keypress() -> str:
    """Waits for a single keypress on stdin.
    -- from :: https://dev59.com/XXNA5IYBdhLWcg3wYMx-#6599441
    """

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

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

def _can_you_vote() -> str:
    """a practical example:
    test if a user can vote based purely on keypresses"""
    print("can you vote? age : ", end="")
    x = int("0" + until_not_multi("0123456789"))
    if not x:
        print("\nsorry, age can only consist of digits.")
        return
    print("your age is", x, "\nYou can vote!" if x >= 18 else "Sorry! you can't vote")

_can_you_vote()

您可以在这里找到完整的模块。
示例:
$ ./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()函数合并,以仅允许三位数字的输入。

如果你已经在检测按键,为什么还要允许字符而且报错呢?你可以安静地忽略它们,直到达到所需的数量。 - Kebman
1
@Kebman,你可以这样做,但用户可能不太清楚他们可以输入什么。 - cat

6
使用try-except来处理错误并再次重复它:
while True:
    try:
        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.")
    except Exception as e:
        print("please enter number")

4
你缺少了一个 break 语句,而 print("please enter number") 是不必要的。 - Georgy

5

在Daniel Q和Patrick Artner的优秀建议基础上,这里提供了更加通用的解决方案。

# 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,因为断言检查可能被关闭,而验证始终应该打开以提供稳健性。

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

# 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!"})

或者,回答原始问题:
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.")

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接