首页 文章

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

提问于
浏览
429

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

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

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

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

但如果他们犯了错误,那就崩溃了:

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'

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

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 之类的值,但在此上下文中却没有意义呢?

16 回答

  • 532

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

    注意:阅读注释顶部的代码 .

    # 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.")
    
  • 16

    为什么你会做一个 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.
    

    这将有效,因为年龄永远不会有一个没有意义的 Value ,代码遵循你的“业务流程”的逻辑

  • 26

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

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

    使用try and 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.
            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:
                        print(template.format(" or ".join((", ".join(map(str,
                                                                         range_[:-1])),
                                                           str(range_[-1])))))
            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'))
    

    常见的陷阱,为什么你应该避免它们

    冗余使用冗余输入语句

    这种方法有效但通常被认为是不好的风格:

    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 终止 . 你可能会想到"no fool would make 1000 mistakes in a row",但是你低估了傻瓜的聪明才智!

  • 8

    使用针对整数输入的自定义 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
    
  • 0

    试试这个: -

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

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

    #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

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

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

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

    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("\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()
    

    你可以找到完整的模块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 之后输入的东西没有输入就会关闭stdin,但我需要在数字之后 .

    您可以将它与同一模块中的 thismany() 函数合并为仅允许三位数 .

  • 1
    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."
    
  • 0

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

    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.
    
  • 0

    虽然接受的答案是惊人的 . 我还想分享这个问题的快速入侵 . (这也解决了负面年龄问题 . )

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

    附:此代码适用于python 2.x,可以通过更改raw_input和print函数导出到3.x.

  • 0

    编辑代码并修复错误:

    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")
    
  • 0

    您可以将输入语句设置为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变量只是如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票” .

  • 0

    在循环中使用try catch with never ending . 要检查空字符串,请使用IF语句检查字符串是否为空 .

    while True:
        name = input("Enter Your Name\n")
        if not name:
            print("I did not understood that")
            continue
        else:
            break
    
    while True:
        try:
            salary = float(input("whats ur salary\n"))
        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 +  "\nYour salary is " + str(salary) + '\nand you will be ' + str(age+1) +' in a Year')
    
  • 0

    这是一个更清晰,更通用的解决方案,可以避免重复的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)])
    

    用法:

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

相关问题