【问题标题】:Asking the user for input until they give a valid response要求用户输入,直到他们给出有效响应
【发布时间】:2022-05-23 03:18:13
【问题描述】:

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

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

【问题讨论】:

    标签: python validation input


    【解决方案1】:

    完成此操作的最简单方法是将 input 方法放入 while 循环中。输入错误时使用continue,满意时使用break退出循环。

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

    使用try and except 来检测用户何时输入无法解析的数据。

    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 方法更短,但它违反了软件开发的 Don't Repeat Yourself 原则。这会增加系统中出现错误的可能性。如果您想通过将 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 次错误”,但你低估了傻子的聪明才智!

    【讨论】:

    • 阅读它有很多例子很有趣,荣誉。被低估的教训:“不要低估傻瓜的聪明才智!”
    • 无论如何,我不仅会赞成这两个问答,因为它们很棒,而且你与“dickety six”达成了协议。干得好,@凯文。
    • 不要估计傻瓜的聪明才智……和聪明的攻击者。对于这种事情,DOS 攻击是最容易的,但其他人可能也是可能的。
    • @JArunMani 我认为它的风格不会很差,但可读性可能会差一些。你确实每个循环只有一个input,循环会变得很短,但条件可能会变得很长......
    • @laundmo,当然,我将编写的代码块发布到公共领域。未经我的明确许可或不知情,请随意在任何情况下使用它们。关于非代码块部分,如果您想将我的整个答案粘贴到您正在编写的“学习 Python”一书中,让我们谈谈版税 ;-)
    【解决方案2】:

    你为什么要执行 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。
    【解决方案3】:

    Functional approach 或“看妈妈没有循环!“:

    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('
    '.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: "))
      
      itertools.chainitertools.repeat 的组合将创建一个迭代器 这将产生字符串 "Enter a number: " 一次,和 "Not a number! Try again: " 无数次:
      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) - 这里map 会将上一步中的所有prompts 字符串应用到input 函数。例如。:
      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. 我们使用filterstr.isdigit过滤掉那些只包含数字的字符串:
      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...
      
      为了只获取第一个只有数字的字符串,我们使用next

      其他验证规则:

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

      2. 会员测试:
        有几种不同的方法来执行它。其中之一是使用__contains__ 方法:

        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. 数字比较:
        我们可以在这里使用有用的比较方法。例如,对于 __lt__ (&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
        

        或者,如果您不喜欢使用双下划线方法(双下划线 = 双下划线),您始终可以定义自己的函数,或使用 operator 模块中的方法。

      4. 路径存在:
        这里可以使用pathlib库及其Path.exists方法:

        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
        

        如果您有许多函数可用于预处理,使用执行 function composition 的函数可能更容易。例如,使用来自here 的那个:

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

        但是在有很多规则的情况下,最好实现一个执行logical conjunction的函数。在下面的示例中,我将使用来自here 的现成的:

        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('
        '.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
        

        不幸的是,如果有人需要为每个失败的案例定制一条消息,那么恐怕没有漂亮的功能方式。或者,至少,我找不到。

    【讨论】:

    • 多么详尽而精彩的答案,解释分解很棒。
    • 使用您的风格,人们将如何去除空格和小写成员测试的输入?我不想创建一个必须包含大写和小写示例的集合。我还想允许空格输入错误。
    • @Austin 我添加了一个关于预处理的新部分。看一看。
    • 这让我想起了 ReactiveX。但也许这首先是受到函数式语言的启发?
    • 感谢你的回答。永远不会想到以这种方式组合迭代器、映射和输入。让我大吃一惊。 lambda 不能很好地与过滤器一起使用吗?
    【解决方案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。

    【讨论】:

    • 请注意,此代码是递归的,但递归在这里不是必需的,正如凯文所说,它可能会破坏您的堆栈。
    • @PM2Ring - 你是对的。但我在这里的目的只是展示“短路”如何最小化(美化)长代码。
    • 为什么要将 lambda 分配给变量,只需使用 def 代替。 def f(age):f = lambda age:清晰多了
    • 在某些情况下,您可能只需要一次年龄,然后就没有使用该功能。人们可能想使用一个函数,并在完成工作后将其丢弃。此外,这可能不是最好的方法,但它绝对是一种不同的方法(这是我的解决方案的目的)。
    • @aaveg 您将如何转换此代码以实际保存用户提供的年龄?
    【解决方案5】:

    使用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
    

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

    检查一个值是否在一个范围内:

    提供了不同的custom types。要获取特定范围内的数字,我们可以使用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
    

    默认值:

    在这种情况下,只需按下进入(或您使用的任何键)不输入值,将为您提供默认值:

    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
    

    【讨论】:

    • 谢谢,这是完美的。在有效范围内循环选择数字正是我想要的。
    【解决方案6】:

    我是 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 并在终止应用程序之前打印友好的退出消息。如果需要,可以使用计数器来限制允许的重试次数。

    【讨论】:

      【解决方案7】:

      所以,我最近在弄乱与此类似的东西,我想出了以下解决方案,它使用一种方法来获取拒绝垃圾的输入,甚至在以任何逻辑方式检查之前。

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

      def read_single_keypress() -> str:
          """Waits for a single keypress on stdin.
          -- from :: https://stackoverflow.com/a/6599441/4532996
          """
      
          import termios, fcntl, sys, os
          fd = sys.stdin.fileno()
          # save old state
          flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
          attrs_save = termios.tcgetattr(fd)
          # make raw - the way to do this comes from the termios(3) man page.
          attrs = list(attrs_save) # copy the stored version to update
          # iflag
          attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                        | termios.ISTRIP | termios.INLCR | termios. IGNCR
                        | termios.ICRNL | termios.IXON )
          # oflag
          attrs[1] &= ~termios.OPOST
          # cflag
          attrs[2] &= ~(termios.CSIZE | termios. PARENB)
          attrs[2] |= termios.CS8
          # lflag
          attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                        | termios.ISIG | termios.IEXTEN)
          termios.tcsetattr(fd, termios.TCSANOW, attrs)
          # turn off non-blocking
          fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
          # read a single keystroke
          try:
              ret = sys.stdin.read(1) # returns a single character
          except KeyboardInterrupt:
              ret = 0
          finally:
              # restore old state
              termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
              fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
          return ret
      
      def until_not_multi(chars) -> str:
          """read stdin until !(chars)"""
          import sys
          chars = list(chars)
          y = ""
          sys.stdout.flush()
          while True:
              i = read_single_keypress()
              _ = sys.stdout.write(i)
              sys.stdout.flush()
              if i not in chars:
                  break
              y += i
          return y
      
      def _can_you_vote() -> str:
          """a practical example:
          test if a user can vote based purely on keypresses"""
          print("can you vote? age : ", end="")
          x = int("0" + until_not_multi("0123456789"))
          if not x:
              print("
      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()
      

      你可以找到完整的模块here

      例子:

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

      请注意,此实现的本质是它会在读取非数字内容后立即关闭标准输入。我没有在a 之后按回车,但我需要在数字之后按。

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

      【讨论】:

      • 如果您已经在检测击键,为什么完全允许字符并抛出错误,而您可以静静地忽略它们,直到获得所需的数字?
      • @Kebman 你可以这样做,但用户可能不太清楚他们可以输入什么
      【解决方案8】:

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

      【讨论】:

      • 您缺少 break 声明,并且 print("please enter number") 是不必要的。
      【解决方案9】:

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

      【讨论】:

        【解决方案10】:
        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."
        

        【讨论】:

          【解决方案11】:

          好问题!您可以为此尝试以下代码。 =)

          此代码使用 ast.literal_eval()查找输入的数据类型age)。然后它遵循以下算法:

          1. 要求用户输入她/他的age

            1.1.如果agefloatint数据类型:

            • 检查是否age&gt;=18。如果是age&gt;=18,打印适当的输出并退出。

            • 检查是否0&lt;age&lt;18。如果是0&lt;age&lt;18,打印适当的输出并退出。

            • 如果age&lt;=0,要求用户再次输入有效的年龄数字,(IE。返回步骤 1。)

            1.2.如果age不是floatint数据类型,则要求用户再次输入她/他的年龄(IE。返回步骤 1。)

            这是代码。

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

          【讨论】:

            【解决方案12】:

            试试这个:-

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

            【讨论】:

              【解决方案13】:

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

              注意:阅读 cmets 代码顶部。

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

              【讨论】:

                【解决方案14】:

                您始终可以应用简单的 if-else 逻辑,并在您的代码中添加一个 if 逻辑以及一个 for 循环。

                while True:
                     age = int(input("Please enter your age: "))
                     if (age >= 18)  : 
                         print("You are able to vote in the United States!")
                     if (age < 18) & (age > 0):
                         print("You are not able to vote in the United States.")
                     else:
                         print("Wrong characters, the input must be numeric")
                         continue
                

                这将是一个无限厕所,你会被要求无限期地进入这个时代。

                【讨论】:

                • 这并不能真正回答问题。问题是关于获取用户输入直到他们给出了有效的回应,而不是无限期地.
                【解决方案15】:

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

                【讨论】:

                  【解决方案16】:

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

                  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.
                  

                  【讨论】:

                  • 您忘记在每次循环后增加 iCount 值
                  【解决方案17】:

                  您可以使输入语句成为 while 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.")
                  

                  var 变量只是为了让如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票”。

                  【讨论】:

                    【解决方案18】:

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

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

                    用法:

                    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)
                    

                    输出:

                    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
                    

                    【讨论】:

                      【解决方案19】:

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

                      细绳

                      def askName():
                          return input("Write your name: ").strip() or askName()
                      
                      name = askName()
                      

                      整数

                      def askAge():
                          try: return int(input("Enter your age: "))
                          except ValueError: return askAge()
                      
                      age = askAge()
                      

                      最后,问题要求:

                      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)
                      

                      【讨论】:

                        【解决方案20】:

                        您可以尝试将其转换为整数,但如果不起作用,请让用户重复。

                        while True:
                            age = input('Please enter your age: ')
                            try:
                                age_int = int(age)
                                if age_int >= 18:
                                    print('You can vote in the United States!')
                                else:
                                    print('You cannot vote in the United States.')
                                break
                            except:
                                print('Please enter a meaningful answer.')
                                
                        

                        只要用户没有输入有意义的答案,while 循环就会运行,但如果有意义则中断。

                        【讨论】:

                          【解决方案21】:

                          使用 isdigit() 检查字符串是否表示有效整数。

                          您可以使用递归函数。

                          def ask():
                              answer = input("Please enter amount to convert: ")
                              if not answer.isdigit():
                                  print("Invalid")
                                  return ask()
                          
                              return int(answer)
                          
                          Gdp = ask()
                          

                          或者 while 循环

                          while True:
                              answer = input("Please enter amount to convert: ")
                              if not answer.isdigit():
                                  print("Invalid")
                                  continue
                          
                              Gbp = int(answer)
                          

                          【讨论】:

                          • 您在函数中缺少 return。您 return 递归调用,但该调用返回 None... 而您 while 循环是无限的...
                          • @Tomerikoo 它递归地询问直到答案有效,我认为这是被问到的。我打算以一种可以放置任何代码的方式编写它里面递归函数或 while 循环。这实际上是为一个不同的问题而写的,它被标记为与这个问题重复,所以我把它贴在这里。
                          • 我的意思是你应该用一些场景来测试你的代码。在第一种情况下,Gbp = int(answer) 可能应该是 return int(answer),而在第二种情况下,可能应该是 break 某处
                          【解决方案22】:

                          下面的代码可能会有所帮助。

                          age=(lambda i,f: f(i,f))(input("Please enter your age: "),lambda i,f: i if i.isdigit() else f(input("Please enter your age: "),f))
                          print("You are able to vote in the united states" if int(age)>=18 else "You are not able to vote in the united states",end='')
                          

                          如果你想有最大的尝试,比如 3,使用下面的代码

                          age=(lambda i,n,f: f(i,n,f))(input("Please enter your age: "),1,lambda i,n,f: i if i.isdigit() else (None if n==3 else f(input("Please enter your age: "),n+1,f)))
                          print("You are able to vote in the united states" if age and int(age)>=18 else "You are not able to vote in the united states",end='')
                          

                          注意:这使用递归。

                          【讨论】:

                          • 不要使用递归来收集用户输入。如果重试次数足够多,应用程序就会崩溃。我不明白打高尔夫球的代码。为什么不让它易于理解?
                          • 向新用户教授这个而不是简单的 while 循环是令人困惑和困惑的。
                          猜你喜欢
                          • 2022-05-23
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多