Запрашиваем ввод у пользователя до тех пор, пока он не введет корректный ответ
Проблема с вводом пользователя в 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 ответ(ов)
Причина, по которой вы можете использовать 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
, вы четко показываете, что продолжаете запрашивать ввод до тех пор, пока не получите валидное значение. Это делает логику вашего кода более понятной для других разработчиков, которые могут читать ваш код в будущем.
Так, если вы считаете, что условие завершения цикла легче понять, это вполне допустимый вариант. Главное – это не только работоспособность кода, но и его поддерживаемость и читаемость. Каждый стиль имеет свои достоинства, и лучший выбор будет зависеть от конкретной ситуации и предпочтений команды.
Хотя принятый ответ просто великолепен, я также хотел бы поделиться небольшим хаком для этой задачи. (Это также решает проблему с отрицательным возрастом.)
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.
Я большой поклонник философии 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
и напечатать дружелюбное сообщение при выходе, прежде чем завершить приложение. Можете использовать счетчик, чтобы ограничить количество попыток ввода, если это потребуется.
Для того чтобы обработать ошибку и повторить ввод информации, можно использовать блок try-except
в цикле while
. Вот пример кода на Python, который делает это:
while True:
try:
age = int(input("Пожалуйста, введите ваш возраст: "))
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
break # Выход из цикла, если ввод был корректным
except ValueError:
print("Пожалуйста, введите число.") # Уточняем, что нужно ввести число
В этом коде мы используем блок try
для попытки преобразования введенного значения в целое число. Если ввод не является числом, возникнет ошибка ValueError
, которая будет обработана в блоке except
, и пользователю будет предложено ввести значение снова. Цикл продолжится до тех пор, пока пользователь не введет корректное число. После успешного ввода программа выходит из цикла с помощью оператора break
.
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("Вы не имеете права голосовать в Соединенных Штатах.")
Ввод данных пользователем и аргументы командной строки [закрыто]
Как изменить порядок столбцов в DataFrame?
Получение текущей даты в формате YYYY-MM-DD в Python
Как удалить пакеты, установленные с помощью easy_install в Python?
Использование @property против геттеров и сеттеров