Запрос пользователя для ввода, пока они не дадут действительный ответ

Я пишу программу, которая должна принимать ввод от пользователя.

#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:PythonProjects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!

но если они ошибаются, то он падает:

C:PythonProjects> 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:PythonProjects> 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, но бессмысленно в этом контекст?

13 ответов


самый простой способ сделать это было бы поставить input метод в цикле while. Использовать continue когда вы получаете плохой вход, и break выход из цикла, когда вы удовлетворены.

когда ваш ввод может вызвать исключение

использовать try и Catch чтобы определить, когда пользователь вводит данные, которые не могут быть разобраны.

while True:
    try:
        # Note: Python 2.x users should use raw_input, the equivalent of 3.x's input
        age = int(input("Please enter your age: "))
    except ValueError:
        print("Sorry, I didn't understand that.")
        #better try again... Return to the start of the loop
        continue
    else:
        #age was successfully parsed!
        #we're ready to exit the loop.
        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'))

общие подводные камни, и почему вы должны избегать их

избыточное использование избыточного 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 метод, но он нарушает не повторяйся принцип разработки программного обеспечения. Это увеличивает вероятность ошибок в вашей системе. Что делать, если вы хотите вернуться к 2.7, изменив input to raw_input, но случайно изменить только первый 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 ошибок подряд", но вы недооцениваете изобретательность дураков!


почему вы сделали 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.

это будет работать, так как возраст никогда не будет иметь значения, которое не будет иметь смысла, и код следует логике вашего "бизнес-процесса"


хотя принятый ответ удивителен. Я также хотел бы поделиться быстрым взломом для этой проблемы. (Это также решает проблему негативного возраста.)

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

P. S. Это код для Python 2.x и может быть экспортирован в 3.x путем изменения функции raw_input и печати.


Итак, я возился с чем-то похожим на это недавно, и я придумал следующее решение, которое использует способ получения ввода, который отклоняет мусор, прежде чем он даже будет проверен каким-либо логическим способом.

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

вы можете найти полный модуль здесь.

пример:

$ ./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, как только что-то, что не является цифрой, читается. Я не нажал enter после a, но мне нужно было после чисел.

вы можете объединить это с thismany() функция в том же модуле, чтобы разрешить только, скажем, три цифры.


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

попробуйте это:-

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

чтобы отредактировать код и исправить ошибку:

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

вы можете написать более общую логику, чтобы позволить пользователю вводить только определенное количество раз, так как один и тот же прецедент возникает во многих реальных приложениях.

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.

Используйте оператор "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.")

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

вы можете сделать оператор ввода a while True loop, чтобы он неоднократно запрашивал ввод пользователей, а затем разорвать этот цикл, если пользователь вводит ответ, который вы хотите. И вы можете использовать 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 такова, что если пользователь вводит строку вместо целого числа, программа не будет возвращать "Вы не можете голосовать в Соединенных Штатах."


используйте try catch с бесконечным циклом while. Чтобы проверить пустую строку, используйте оператор 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')

это будет продолжать просить пользователя ввести номер, пока они не введут допустимое число:

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