7

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

3

Проблема с вводом пользователя в Python: необходимо обработать некорректные данные

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

# Примечание: пользователям Python 2.7 следует использовать `raw_input`, аналог `input` в 3.X
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.")

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

Пожалуйста, введите ваш возраст: 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)?

5 ответ(ов)

0

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

В приведенном вами примере кода, хотя можно переписать цикл так:

while True:
    input_value = input("Please enter your age: ")
    try:
        age = int(input_value)
        break  # Прерываем цикл, если возраст успешно введен
    except ValueError:
        print("{input} is not a number, please enter a number only".format(input=input_value))

Тем не менее, такой подход также приводит к использованию break, который может ввести в заблуждение, если вы не знакомы с этим стилем.

При использовании while age is None, вы четко показываете, что продолжаете запрашивать ввод до тех пор, пока не получите валидное значение. Это делает логику вашего кода более понятной для других разработчиков, которые могут читать ваш код в будущем.

Так, если вы считаете, что условие завершения цикла легче понять, это вполне допустимый вариант. Главное – это не только работоспособность кода, но и его поддерживаемость и читаемость. Каждый стиль имеет свои достоинства, и лучший выбор будет зависеть от конкретной ситуации и предпочтений команды.

0

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

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

P.S. Этот код предназначен для Python 3.x.

0

Я большой поклонник философии 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 и напечатать дружелюбное сообщение при выходе, прежде чем завершить приложение. Можете использовать счетчик, чтобы ограничить количество попыток ввода, если это потребуется.

0

Для того чтобы обработать ошибку и повторить ввод информации, можно использовать блок try-except в цикле while. Вот пример кода на Python, который делает это:

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

В этом коде мы используем блок try для попытки преобразования введенного значения в целое число. Если ввод не является числом, возникнет ошибка ValueError, которая будет обработана в блоке except, и пользователю будет предложено ввести значение снова. Цикл продолжится до тех пор, пока пользователь не введет корректное число. После успешного ввода программа выходит из цикла с помощью оператора break.

0

Building upon предложениями Даниэля Q и Патрика Артнера, я предлагаю еще более обобщенное решение.

# Предполагается использование Python 3
import sys

class ValidationError(ValueError):  # спасибо Патрику Артнеру
    pass

def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None):
    if onerror is 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("Вы не имеете права голосовать в Соединенных Штатах.")
Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь