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

Вопрос или проблема

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

#Примечание: пользователи Python 2.7 должны использовать `raw_input`, аналог 3.X `input`
age = int(input("Пожалуйста, введите ваш возраст: "))
if age >= 18: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

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

Пожалуйста, введите ваш возраст: 23
Вы можете голосовать в Соединенных Штатах!

Но она не работает, если пользователь вводит недопустимые данные:

Пожалуйста, введите ваш возраст: dickety six
Traceback (most recent call last):
  File "canyouvote.py", line 1, in <module>
    age = int(input("Пожалуйста, введите ваш возраст: "))
ValueError: invalid literal for int() with base 10: 'dickety six'

Вместо того, чтобы выдавать ошибку, я хотел бы, чтобы программа снова запрашивала ввод. Вот так:

Пожалуйста, введите ваш возраст: dickety six
Извините, я не понял это.
Пожалуйста, введите ваш возраст: 26
Вы можете голосовать в Соединенных Штатах!

Как мне запросить действительный ввод вместо того, чтобы выдавать ошибку или принимать недопустимые значения (например, -1)?

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

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

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

while True:
    try:
        # Примечание: пользователи Python 2.x должны использовать raw_input, аналог 3.x input
        age = int(input("Пожалуйста, введите ваш возраст: "))
    except ValueError:
        print("Извините, я не понял это.")
        # лучше попробовать еще раз... Вернуться в начало цикла
        continue
    else:
        # возраст успешно распознан!
        # мы готовы выйти из цикла.
        break
if age >= 18: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

Реализация собственных правил валидации

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

while True:
    data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")
    if not data.isupper():
        print("Извините, ваш ответ был недостаточно громким.")
        continue
    else:
        # нам нравится данное значение.
        # мы готовы выйти из цикла.
        break

while True:
    data = input("Выберите ответ от A до D:")
    if data.lower() not in ('a', 'b', 'c', 'd'):
        print("Это не подходящий выбор.")
    else:
        break

Сочетание обработки исключений и пользовательской валидации

Обе вышеперечисленные техники могут быть объединены в один цикл.

while True:
    try:
        age = int(input("Пожалуйста, введите ваш возраст: "))
    except ValueError:
        print("Извините, я не понял это.")
        continue

    if age < 0:
        print("Извините, ваш ответ не должен быть отрицательным.")
        continue
    else:
        # возраст успешно распознан, и нам нравится его значение.
        # мы готовы выйти из цикла.
        break
if age >= 18: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

Инкапсуляция всего в функции

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

def get_non_negative_int(prompt):
    while True:
        try:
            value = int(input(prompt))
        except ValueError:
            print("Извините, я не понял это.")
            continue

        if value < 0:
            print("Извините, ваш ответ не должен быть отрицательным.")
            continue
        else:
            break
    return value

age = get_non_negative_int("Пожалуйста, введите ваш возраст: ")
kids = get_non_negative_int("Пожалуйста, введите количество детей, которые у вас есть: ")
salary = get_non_negative_int("Пожалуйста, введите ваши годовые доходы в долларах: ")

Собираем все вместе

Вы можете расширить эту идею, чтобы сделать очень универсальную функцию ввода:

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_ должно быть меньше или равно max_.")
    while True:
        ui = input(prompt)
        if type_ is not None:
            try:
                ui = type_(ui)
            except ValueError:
                print("Тип ввода должен быть {0}.".format(type_.__name__))
                continue
        if max_ is not None and ui > max_:
            print("Ввод должен быть меньше или равен {0}.".format(max_))
        elif min_ is not None and ui < min_:
            print("Ввод должен быть больше или равен {0}.".format(min_))
        elif range_ is not None and ui not in range_:
            if isinstance(range_, range):
                template = "Ввод должен быть между {0.start} и {0.stop}."
                print(template.format(range_))
            else:
                template = "Ввод должен быть {0}."
                if len(range_) == 1:
                    print(template.format(*range_))
                else:
                    expected = " или ".join((
                        ", ".join(str(x) for x in range_[:-1]),
                        str(range_[-1])
                    ))
                    print(template.format(expected))
        else:
            return ui

С использованием, например:

age = sanitised_input("Введите ваш возраст: ", int, 1, 101)
answer = sanitised_input("Введите ваш ответ: ", str.lower, range_=('a', 'b', 'c', 'd'))

Распространенные ошибки и почему их следует избегать

Избыточное использование избыточных input операторов

Этот метод работает, но обычно считается плохим стилем:

data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")
while not data.isupper():
    print("Извините, ваш ответ был недостаточно громким.")
    data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")

Это может казаться привлекательным изначально, потому что короче, чем метод while True, но он нарушает принцип Не повторяй себя в разработке программного обеспечения. Это увеличивает вероятность ошибок в вашей системе. Что, если вы захотите перейти на 2.7, изменив input на raw_input, но случайно измените только первый input выше? Это SyntaxError, который только и ждет, чтобы возникнуть.

Рекурсия убьет ваш стек

Если вы только что узнали о рекурсии, вы можете быть склонны использовать ее в get_non_negative_int, чтобы избавиться от цикла while.

def get_non_negative_int(prompt):
    try:
        value = int(input(prompt))
    except ValueError:
        print("Извините, я не понял это.")
        return get_non_negative_int(prompt)

    if value < 0:
        print("Извините, ваш ответ не должен быть отрицательным.")
        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("Пожалуйста, введите ваш возраст: ")
    try:
        # попытка конвертировать строковый ввод в число
        age = int(input_value)
    except ValueError:
        # сказать пользователю
        print("{input} не является числом, пожалуйста, введите только число".format(input=input_value))
if age >= 18:
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

Это приведет к следующему:

Пожалуйста, введите ваш возраст: *картошка*
картошка не является числом, пожалуйста, введите только число
Пожалуйста, введите ваш возраст: *5*
Вы не можете голосовать в Соединенных Штатах.

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

Функциональный подход или “смотри, мама, никаких циклов!“:

from itertools import chain, repeat

prompts = chain(["Введите число: "], repeat("Это не число! Попробуйте снова: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Введите число:  a
Это не число! Попробуйте снова:  b
Это не число! Попробуйте снова:  1
1

или если вы хотите, чтобы сообщение “неверный ввод” было отделено от сообщения с запросом, как в других ответах:

prompt_msg = "Введите число: "
bad_input_msg = "Извините, я не понял это."
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)
Введите число:  a
Извините, я не понял это.
Введите число:  b
Извините, я не понял это.
Введите число:  1
1

Как это работает?

  1. prompts = chain(["Введите число: "], repeat("Это не число! Попробуйте снова: "))
    

    Это сочетание itertools.chain и itertools.repeat создаст итератор
    который будет возвращать строки "Введите число: " один раз, и "Это не число! Попробуйте снова: " бесконечное число раз:

    for prompt in prompts:
        print(prompt)
    
    Введите число: 
    Это не число! Попробуйте снова: 
    Это не число! Попробуйте снова: 
    Это не число! Попробуйте снова: 
    # ... и так далее
    
  2. replies = map(input, prompts) – здесь map применит все строки prompts из предыдущего шага к функции input. Например:
    for reply in replies:
        print(reply)
    
    Введите число:  a
    a
    Это не число! Попробуйте снова:  1
    1
    Это не число! Попробуйте снова:  это не важно
    это не важно
    # и так далее...
    
  3. Мы используем filter и str.isdigit, чтобы отфильтровать те строки, которые содержат только цифры:
    only_digits = filter(str.isdigit, replies)
    for reply in only_digits:
        print(reply)
    
    Введите число:  a
    Это не число! Попробуйте снова:  1
    1
    Это не число! Попробуйте снова:  2
    2
    Это не число! Попробуйте снова:  b
    Это не число! Попробуйте снова: # и так далее...
    

    И чтобы получить только первую строку, содержащую лишь цифры, мы используем next.

Другие правила валидации:

  1. Методы строк: Конечно, вы можете использовать другие методы строк, например, str.isalpha, чтобы получить только буквы, или str.isupper, чтобы получить только заглавные буквы. Смотрите документацию для полного списка.

  2. Тестирование членства:
    Существует несколько различных способов его выполнения. Один из них – использование __contains__ метода:

    from itertools import chain, repeat
    
    fruits = {'apple', 'orange', 'peach'}
    prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: "))
    replies = map(input, prompts)
    valid_response = next(filter(fruits.__contains__, replies))
    print(valid_response)
    
    Введите фрукт:  1
    Я не знаю этого! Попробуйте снова:  foo
    Я не знаю этого! Попробуйте снова:  apple
    apple
    
  3. Сравнение чисел:
    Существуют полезные методы сравнения, которые мы можем использовать здесь. Например, для __lt__ (<):

    from itertools import chain, repeat
    
    prompts = chain(["Введите положительное число:"], repeat("Мне нужно положительное число! Попробуйте снова:"))
    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)
    
    Введите положительное число: a
    Мне нужно положительное число! Попробуйте снова: -5
    Мне нужно положительное число! Попробуйте снова: 0
    Мне нужно положительное число! Попробуйте снова: 5
    5.0
    

    Или, если вам не нравятся дандер-методы (дуnder = двойное подчеркивание), вы всегда можете определить свою собственную функцию или использовать функции из модуля operator.

  4. Проверка существования пути:
    Здесь можно использовать библиотеку pathlib и ее Path.exists метод:

    from itertools import chain, repeat
    from pathlib import Path
    
    prompts = chain(["Введите путь: "], repeat("Этот путь не существует! Попробуйте снова: "))
    replies = map(input, prompts)
    paths = map(Path, replies)
    valid_response = next(filter(Path.exists, paths))
    print(valid_response)
    
    Введите путь:  a b c
    Этот путь не существует! Попробуйте снова:  1
    Этот путь не существует! Попробуйте снова:  existing_file.txt
    existing_file.txt
    

Ограничение количества попыток:

Если вы не хотите мучить пользователя, задавая ему вопрос бесконечное количество раз, вы можете указать лимит в вызове itertools.repeat. Это можно комбинировать с предоставлением значения по умолчанию для функции next:

from itertools import chain, repeat

prompts = chain(["Введите число:"], repeat("Это не число! Попробуйте снова:", 2))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies), None)
print("Вы miserably провалились!" if valid_response is None else 'Отлично!')
Введите число: a
Это не число! Попробуйте снова: b
Это не число! Попробуйте снова: c
Вы miserably провалились!

Предварительная обработка входных данных:

Иногда мы не хотим отклонять ввод, если пользователь случайно предоставил его ВСЕМИ ЗАГЛАВНЫМИ или с пробелом в начале или конце строки. Чтобы учитывать эти простые ошибки, мы можем предварительно обработать ввод, применив str.lower и str.strip методы. Например, в случае проверки членства код будет выглядеть так:

from itertools import chain, repeat

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: "))
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)
Введите фрукт:  duck
Я не знаю этого! Попробуйте снова:     Orange
orange

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

from itertools import chain, repeat

from lz.functional import compose

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower)  # здесь вы можете добавить больше функций
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)
Введите фрукт:  potato
Я не знаю этого! Попробуйте снова:   PEACH
peach

Комбинирование правил валидации:

Для простого случая, например, когда программа запрашивает возраст от 1 до 120, можно просто добавить еще один filter:

from itertools import chain, repeat

prompt_msg = "Введите ваш возраст (1-120): "
bad_input_msg = "Неправильный ввод."
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 = "Введите букву (C-P): "
bad_input_msg = "Неправильный ввод."
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)
Введите букву (C-P):  5
Неправильный ввод.
Введите букву (C-P):  f
Неправильный ввод.
Введите букву (C-P):  CDE
Неправильный ввод.
Введите букву (C-P):  Q
Неправильный ввод.
Введите букву (C-P):  N
N

К сожалению, если кто-то нуждается в настраиваемом сообщении для каждого случая неудачи, то боюсь, что нет красивого функционального способа. Или, по крайней мере, я не смог найти его.

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

f=lambda age: (age.isdigit() and ((int(age)>=18  and "Может голосовать" ) or "Не может голосовать")) or \
f(input("недопустимый ввод. Попробуйте еще раз\nПожалуйста, введите ваш возраст: "))
print(f(input("Пожалуйста, введите ваш возраст: ")))

П.С. Этот код написан для python 3.x.

Используя Click:

Click – это библиотека для интерфейсов командной строки, и она предоставляет функциональность для запроса действительного ответа от пользователя.

Простой пример:

import click

number = click.prompt('Пожалуйста, введите число', type=float)
print(number)
Пожалуйста, введите число: 
 a
Ошибка: a не является действительным числом с плавающей точкой
Пожалуйста, введите число: 
 10
10.0

Обратите внимание, как он автоматически преобразовал строковое значение в число с плавающей точкой.

Проверка, находится ли значение в пределах диапазона:

Существует разные пользовательские типы. Чтобы получить число в определенном диапазоне, мы можем использовать IntRange:

age = click.prompt("Сколько вам лет?", type=click.IntRange(1, 120))
print(age)
Сколько вам лет?: 
 a
Ошибка: a не является действительным целым числом
Сколько вам лет?: 
 0
Ошибка: 0 не находится в допустимом диапазоне от 1 до 120.
Сколько вам лет?: 
 5
5

Мы также можем указать только один из пределов, min или max:

age = click.prompt("Сколько вам лет?", type=click.IntRange(min=14))
print(age)
Сколько вам лет?: 
 0
Ошибка: 0 меньше минимально допустимого значения 14.
Сколько вам лет?: 
 18
18

Тестирование членства:

Используя тип click.Choice. По умолчанию эта проверка регистронезависимая.

choices = {'apple', 'orange', 'peach'}
choice = click.prompt('Укажите фрукт', type=click.Choice(choices, case_sensitive=False))
print(choice)
Укажите фрукт (apple, peach, orange): 
 banana
Ошибка: недопустимый выбор: banana. (выберите из apple, peach, orange)
Укажите фрукт (apple, peach, orange): 
 OrAnGe
orange

Работа с путями и файлами:

Используя тип click.Path, мы можем проверять существующие пути, а также разрешать их:

path = click.prompt('Укажите путь', type=click.Path(exists=True, resolve_path=True))
print(path)
Укажите путь: 
 nonexistent
Ошибка: Путь "nonexistent" не существует.
Укажите путь: 
 existing_folder
'/path/to/existing_folder

Чтение и запись файлов можно осуществить с помощью click.File:

file = click.prompt('В какой файл записать данные?', type=click.File('w'))
with file.open():
    file.write('Hello!')
# Дополнительная информация о `lazy=True` на:
# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safety
file = click.prompt('Какой файл вы хотите прочитать?', type=click.File(lazy=True))
with file.open():
    print(file.read())
В какой файл записать данные?: 
         # <-- ввел пустую строку, что является недопустимым именем файла
В какой файл записать данные?: 
 some_file.txt
Какой файл вы хотите прочитать?: 
 nonexistent.txt
Ошибка: Не удалось открыть файл: nonexistent.txt: Нет такого файла или директории
Какой файл вы хотите прочитать?: 
 some_file.txt
Hello!

Другие примеры:

Подтверждение пароля:

password = click.prompt('Введите пароль', hide_input=True, confirmation_prompt=True)
print(password)
Введите пароль: 
 ······
Повторите для подтверждения: 
 ·
Ошибка: два введенных значения не совпадают
Введите пароль: 
 ······
Повторите для подтверждения: 
 ······
qwerty

Значения по умолчанию:

В этом случае простое нажатие Enter (или любой другой клавишы, которую вы используете) без ввода значения даст вам значение по умолчанию:

number = click.prompt('Пожалуйста, введите число', type=int, default=42)
print(number)
Пожалуйста, введите число [42]: 
 a
Ошибка: a не является действительным целым числом
Пожалуйста, введите число [42]: 

42

Я большой поклонник философии Unix “Делай одно и делай это хорошо”. Захват ввода пользователя и его валидация – два отдельных этапа:

  • запрашивать у пользователя ввод с помощью get_input до тех пор, пока ввод не будет корректным
  • валидация с использованием функции validator, которую можно передать в get_input

Это можно сделать так просто (Python 3.8+, с оператором моржа):

def get_input(
    prompt="Введите значение: ",
    validator=lambda x: True,
    error_message="Недопустимый ввод. Пожалуйста, попробуйте снова.",
):
    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("Введите положительное число: ", is_positive_int)
    print(f"Хорошо, спасибо за {val}")

Пример запуска:

Введите положительное число: -5
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число: asdf
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число:
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число: 42
Хорошо, спасибо за 42

В Python < 3.8 вы могли бы использовать get_input вот так:

def get_input(
    prompt="Введите значение: ",
    validator=lambda x: True,
    error_message="Недопустимый ввод. Пожалуйста, попробуйте снова.",
):
    while True:
        value = input(prompt)
        if validator(value):
            return value
        print(error_message)

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

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

read_single_keypress() предоставлено https://stackoverflow.com/a/6599441/4532996

def read_single_keypress() -> str:
    """Ожидает одиночный нажатие клавиши на стандартном вводе.
    -- из :: https://stackoverflow.com/a/6599441/4532996
    """

    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # сохранить старое состояние
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # сделать сырым - способ сделать это взят из man-страницы termios(3).
    attrs = list(attrs_save) # копировать сохраненную версию для обновления
    # 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)
    # отключить неблокирующий режим
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # читать одно нажатие клавиши
    try:
        ret = sys.stdin.read(1) # возвращает один символ
    except KeyboardInterrupt:
        ret = 0
    finally:
        # восстановить старое состояние
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return ret

def until_not_multi(chars) -> str:
    """чтение stdin до !(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:
    """практический пример:
    проверить, может ли пользователь голосовать, основываясь только на нажатия клавиш"""
    print("можете ли вы голосовать? возраст : ", end="")
    x = int("0" + until_not_multi("0123456789"))
    if not x:
        print("\nизвините, возраст может состоять только из цифр.")
        return
    print("ваш возраст", x, "\nВы можете голосовать!" if x >= 18 else "Извините! Вы не можете голосовать")

_can_you_vote()

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

Пример:

$ ./input_constrain.py
можете ли вы голосовать? возраст : a
извините, возраст может состоять только из цифр.
$ ./input_constrain.py 
можете ли вы голосовать? возраст : 23<RETURN>
ваш возраст 23
Вы можете голосовать!
$ _

Обратите внимание, что специфика этой реализации заключается в том, что она закрывает stdin сразу, как только что-то, что не является цифрой, будет прочитано. Я не нажал Enter после a, но мне нужно было это сделать после цифр.

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

Используйте try-except, чтобы обработать ошибку и повторите ее снова:

while True:
    try:
        age = int(input("Пожалуйста, введите ваш возраст: "))
        if age >= 18:
            print("Вы можете голосовать в Соединенных Штатах!")
        else:
            print("Вы не можете голосовать в Соединенных Штатах.")
    except Exception as e:
        print("пожалуйста, введите число")

Основываясь на отличных предложениях Даниэля Q и Патрика Артнера,
вот еще одно более общее решение.

# Предполагая Python3
import sys

class ValidationError(ValueError):  # спасибо, Патрик Артнер
    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:  # спасибо, Даниэль Q
            print(onerror[type(e)], file=sys.stderr)

Я выбрал явные if и raise операторы вместо assert,
потому что проверка утверждений может быть отключена,
в то время как валидация всегда должна выполняться, чтобы обеспечить надежность.

Это можно использовать для получения различных типов ввода,
с различными условиями валидации.
Например:

# Без валидации, эквивалентно простому вводу:
anystr = validate_input("Введите любую строку: ")

# Получить строку, содержащую только буквы:
letters = validate_input("Введите буквы: ",
    cond=str.isalpha,
    onerror={ValidationError: "Только буквы, пожалуйста!"})

# Получить число с плавающей точкой в [0, 100]:
percentage = validate_input("Процент? ",
    cast=float, cond=lambda x: 0.0<=x<=100.0,
    onerror={ValidationError: "Должен быть между 0 и 100!",
             ValueError: "Не число!"})

Или, чтобы ответить на исходный вопрос:

age = validate_input("Пожалуйста, введите ваш возраст: ",
        cast=int, cond=lambda a:0<=a<150,
        onerror={ValidationError: "Введите правдоподобный возраст, пожалуйста!",
                 ValueError: "Введите целое число, пожалуйста!"})
if age >= 18: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

def validate_age(age):
    if age >=0 :
        return True
    return False

while True:
    try:
        age = int(raw_input("Пожалуйста, введите ваш возраст:"))
        if validate_age(age): break
    except ValueError:
        print "Ошибка: недопустимый возраст."

Хороший вопрос! Вы можете попробовать следующий код для этого. =)

Этот код использует ast.literal_eval() для определения типа данных ввода (age). Затем он следует следующему алгоритму:

  1. Попросите пользователя ввести ее/его age.

    1.1. Если age имеет тип float или int:

    • Проверьте, если age>=18. Если age>=18, выведите соответствующий вывод и завершите.

    • Проверьте, если 0<age<18. Если 0<age<18, выведите соответствующий вывод и завершите.

    • Если age<=0, попросите пользователя снова ввести допустимое число для age, (т.е. вернуться к шагу 1.)

    1.2. Если age не имеет типа float или int, тогда снова попросите пользователя ввести ее/его возраст (т.е. вернуться к шагу 1.)

Вот код.

from ast import literal_eval

''' Эта функция используется для определения типа данных вводимых данных.'''
def input_type(input_data):
    try:
        return type(literal_eval(input_data))
    except (ValueError, SyntaxError):
        return str

flag = True

while(flag):
    age = raw_input("Пожалуйста, введите ваш возраст: ")

    if input_type(age)==float or input_type(age)==int:
        if eval(age)>=18: 
            print("Вы можете голосовать в Соединенных Штатах!") 
            flag = False 
        elif eval(age)>0 and eval(age)<18: 
            print("Вы не можете голосовать в Соединенных Штатах.") 
            flag = False
        else: print("Пожалуйста, введите допустимое число как ваш возраст.")

    else: print("Извините, я не понял это.") 

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

def takeInput(required):
  print 'ooo or OOO to exit'
  ans = raw_input('Введите: ')

  if not ans:
      print "Вы ничего не ввели...!"
      return takeInput(required) 

      ##  ДЛЯ Выхода  ## 
  elif ans in ['ooo', 'OOO']:
    print "Закрытие экземпляра."
    exit()

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

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

## передайте значение, в котором вы хотите [str/int/специальный символ(как другое )]
print "ввод: ", takeInput('str')

Используйте оператор while до тех пор, пока пользователь не введет правильное значение, и если вводимое значение не является числом или имеет нулевое значение, пропустите его и попытайтесь снова и так далее.
В примере я попытался достоверно ответить на ваш вопрос. Если предположим, что наш возраст находится в пределах от 1 до 150, тогда вводимые значения принимаются, иначе это неверное значение.
Для завершения программы пользователь может использовать клавишу 0 и ввести ее как значение.

Примечание: Читайте комментарии вверху кода.

# Если ваше вводимое значение является только числом, тогда используйте "Value.isdigit() == False".
# Если вам нужен ввод, который является текстом, вы должны удалить "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

# Пример:
age = 0
# Если предположим, что наш возраст находится в пределах от 1 до 150, тогда вводимые значения принимаются,
# иначе это неверное значение.
while age <=0 or age >150:
    age = int(Input("Пожалуйста, введите ваш возраст: "))
    # Для завершения программы пользователь может использовать клавишу 0 и ввести ее как значение.
    if age == 0:
        print("Завершение ...")
        exit(0)

if age >= 18 and age <=150: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

Вы всегда можете применить простую if-else логику и добавить еще одну if логику к вашему коду вместе с циклом for.

while True:
     age = int(input("Пожалуйста, введите ваш возраст: "))
     if (age >= 18)  : 
         print("Вы можете голосовать в Соединенных Штатах!")
     if (age < 18) & (age > 0):
         print("Вы не можете голосовать в Соединенных Штатах.")
     else:
         print("Неверные символы, ввод должен быть числовым")
         continue

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

Хотя блок try/except сработает, гораздо более быстрым и чистым способом выполнения этой задачи будет использование str.isdigit().

while True:
    age = input("Пожалуйста, введите ваш возраст: ")
    if age.isdigit():
        age = int(age)
        break
    else:
        print("Неверное число '{age}'. Попробуйте снова.".format(age=age))

if age >= 18: 
    print("Вы можете голосовать в Соединенных Штатах!")
else:
    print("Вы не можете голосовать в Соединенных Штатах.")

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

def getValidInt(iMaxAttemps = None):
  iCount = 0
  while True:
    # выход, когда максимальный лимит попыток истек
    if iCount != None and iCount > iMaxAttemps:
       return 0     # вернуть как значение по умолчанию

    i = raw_input("Введите число")
    try:
       i = int(i)
    except ValueError as e:
       print "Введите корректное целочисленное значение"
    else:
       break

    return i

age = getValidInt()
# сделать все, что хотите.

Вы можете сделать оператор ввода циклом while True, чтобы он постоянно запрашивал ввод пользователя, а затем прерывал этот цикл, если пользователь введет нужный ответ. И вы можете использовать блоки try и except для обработки недопустимых ответов.

while True:

    var = True

    try:
        age = int(input("Пожалуйста, введите ваш возраст: "))

    except ValueError:
        print("Недопустимый ввод.")
        var = False

    if var == True:
        if age >= 18:
                print("Вы можете голосовать в Соединенных Штатах.")
                break
        else:
            print("Вы не можете голосовать в Соединенных Штатах.")

Переменная var нужна только для того, чтобы если пользователь вводит строку вместо целого числа, программа не выдавала “Вы не можете голосовать в Соединенных Штатах.”

Еще одно решение для использования валидации ввода с использованием индивидуального ValidationError и (по желанию) проверки диапазона для целочисленных вводов:

class ValidationError(ValueError): 
    """Специальная ошибка валидации - ее сообщение должно выводиться"""
    pass

def RangeValidator(text,num,r):
    """Общая валидация - вызывает 'text' как ValidationError, если 'num' не в диапазоне 'r'."""
    if num in r:
        return num
    raise ValidationError(text)

def ValidCol(c): 
    """Специальная валидация столбцов, предоставляющая текст и диапазон."""
    return RangeValidator("Столбцы должны находиться в диапазоне от 0 до 3 (включительно)", 
                          c, range(4))

def ValidRow(r): 
    """Специальная валидация строк, предоставляющая текст и диапазон."""
    return RangeValidator("Строки должны находиться в диапазоне от 5 до 15 (исключительно)",
                          r, range(5,15))

Использование:

def GetInt(text, validator=None):
    """Запрашивает у пользователя ввод целого числа до тех пор, пока не будет введено допустимое целое число. Если предоставлено, 
    функция 'validator' берет целое число и либо вызывает 
    ValidationError, чтобы его напечатать, или возвращает допустимое число. 
    Некорректные целые числа выводят простое сообщение об ошибке."""
    print()
    while True:
        n = input(text)
        try:
            n = int(n)

            return n if validator is None else validator(n)

        except ValueError as ve:
            # напрямую выводит ValidationErrors - иначе общее сообщение:
            if isinstance(ve, ValidationError):
                print(ve)
            else:
                print("Недопустимый ввод: ", n)

column = GetInt("Пожалуйста, введите столбец: ", ValidCol)
row = GetInt("Пожалуйста, введите строку: ", ValidRow)
print( row, column)

Результат:

Пожалуйста, введите столбец: 22
Столбцы должны находиться в диапазоне от 0 до 3 (включительно)
Пожалуйста, введите столбец: -2
Столбцы должны находиться в диапазоне от 0 до 3 (включительно)
Пожалуйста, введите столбец: 2
Пожалуйста, введите строку: a
Недопустимый ввод:  a
Пожалуйста, введите строку: 72
Строки должны находиться в диапазоне от 5 до 15 (исключительно)
Пожалуйста, введите строку: 9  

9, 2

Постоянный ввод пользователя с использованием рекурсивной функции:

Строка

def askName():
    return input("Введите ваше имя: ").strip() or askName()

name = askName()

Целое число

def askAge():
    try: return int(input("Введите ваш возраст: "))
    except ValueError: return askAge()

age = askAge()

и наконец, требование вопроса:

def askAge():
    try: return int(input("Введите ваш возраст: "))
    except ValueError: return askAge()

age = askAge()

responseAge = [
    "Вы можете голосовать в Соединенных Штатах!",
    "Вы не можете голосовать в Соединенных Штатах.",
][int(age < 18)]

print(responseAge)

Вы можете попытаться преобразовать это в целое число, но попросите пользователя повторить, если это не сработает.

while True:
    age = input('Пожалуйста, введите ваш возраст: ')
    try:
        age_int = int(age)
        if age_int >= 18:
            print('Вы можете голосовать в Соединенных Штатах!')
        else:
            print('Вы не можете голосовать в Соединенных Штатах.')
        break
    except:
        print('Пожалуйста, введите осмысленный ответ.')

Цикл while работает до тех пор, пока пользователь не введет осмысленный ответ, но завершается, если он имеет смысл.

Используйте isdigit(), чтобы проверить, представляет ли строка действительное целое число.

Вы можете использовать рекурсивную функцию.

def ask():
    answer = input("Пожалуйста, введите сумму для конвертации: ")
    if not answer.isdigit():
        print("Недопустимый ввод")
        return ask()

    return int(answer)

Gdp = ask()

Или цикл while

while True:
    answer = input("Пожалуйста, введите сумму для конвертации: ")
    if not answer.isdigit():
        print("Недопустимый ввод")
        continue

    Gbp = int(answer)

Ниже приведенный код может помочь.

age=(lambda i,f: f(i,f))(input("Пожалуйста, введите ваш возраст: "),lambda i,f: i if i.isdigit() else f(input("Пожалуйста, введите ваш возраст: "),f))
print("Вы можете голосовать в соединенных штатах" if int(age)>=18 else "Вы не можете голосовать в соединенных штатах",end='')

Если хотите ограничить количество попыток, скажем 3, используйте следующий код

age=(lambda i,n,f: f(i,n,f))(input("Пожалуйста, введите ваш возраст: "),1,lambda i,n,f: i if i.isdigit() else (None if n==3 else f(input("Пожалуйста, введите ваш возраст: "),n+1,f)))
print("Вы можете голосовать в соединенных штатах" если age и int(age)>=18 else "Вы не можете голосовать в соединенных штатах",end='')

Примечание: Это использует рекурсию.

Ответ или решение

Чтобы создать программу, которая запрашивает возраст пользователя и продолжает запрашивать ввод до тех пор, пока этот ввод не будет действительным, вы можете использовать цикл while в сочетании с обработкой исключений. Приведённый ниже код демонстрирует эту логику:

while True:
    try:
        age = int(input("Пожалуйста, введите ваш возраст: "))

        # Проверка на минимальный и максимальный возраст
        if age < 0:
            print("Возраст не может быть отрицательным. Пожалуйста, попробуйте снова.")
            continue

        # Взрослый ли пользователь
        if age >= 18:
            print("Вы имеете право голосовать в Соединенных Штатах!")
        else:
            print("Вы не имеете права голосовать в Соединенных Штатах.")
        break  # Выход из цикла, если ввод валиден
    except ValueError:
        print("Извините, я не понял это. Пожалуйста, введите число.")

Объяснение кода:

  1. Цикл while True: Этот цикл будет повторяться до тех пор, пока программой не будет выполнено явное прерывание (например, с помощью break).

  2. Обработка исключений: Используя конструкцию try-except, программа пытается преобразовать ввод пользователя в целое число. Если ввод некорректен (например, если пользователь вводит буквы), программа срабатывает на исключение ValueError и выводит сообщение об ошибке.

  3. Проверка возраста: После успешного преобразования возраста программа проверяет, является ли возраст отрицательным. Если да, выводится сообщение, и пользователь может попробовать снова.

  4. Определение права на голосование: Если возраст корректный и равен или больше 18, пользователю сообщается о праве голоса. В противном случае — о его отсутствии.

  5. Выход из цикла: После корректного ввода и обработки программы происходит выход из цикла break.

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

Оцените материал
Добавить комментарий

Капча загружается...