2025, Nov 20 18:01
Повторный запрос ввода в интерактивных скриптах Python: вложенный цикл вместо пропуска элемента
Разбираем типичную ошибку в интерактивных скриптах Python: пропуск элемента после неверного ввода. Показываем решение с вложенным циклом и безопасным выходом.
Про надёжную проверку ввода в интерактивных скриптах легко забыть. Частая ловушка — цикл переходит к следующему элементу после неверного ввода, незаметно пропуская текущий. Ниже — короткое объяснение, как удерживать запрос на том же элементе, пока не будет введено допустимое значение (или явно не будет выбран выход).
Проблема
Нужно собрать пять оценок, каждая привязана к конкретному разведению. Разрешён только заданный набор строк, а ввод q должен завершать сеанс и показывать уже собранные данные. Текущий подход после некорректного ввода сразу переходит к следующему разведению вместо того, чтобы повторно спросить текущее.
Проблемный код
Следующий фрагмент показывает проблему. Он принимает ввод один раз на каждое разведение, и если значение недопустимо, просто продолжает со следующим разведением, не пытаясь повторить запрос.
series = ["1:16", "1:32", "1:64", "1:128", "1:256"]
permitted = ["+", "++-", "-", "+-", "-a", "A"]
ledger = {}
for ratio in series:
mark = input("Enter score: ")
try:
if mark in permitted:
ledger[ratio] = mark
elif mark == "q":
print("Done!")
break
else:
print("Not a valid score!")
except TypeError:
print("Invalid input! Try again")
break
print(ledger)
Почему это не работает
После неверного значения код выводит сообщение и тут же переходит к следующему разведению во внешнем цикле. Механизма, чтобы повторить запрос для того же разведения, нет. Кроме того, обработка исключений здесь бесполезна, потому что показанная логика не вызывает TypeError: чтение ввода и проверка принадлежности элементу списка не приведут к такому исключению. В итоге некорректные записи оставляют тихие «дыры» в соответствиях между разведениями и оценками.
Решение
Прямолинейное исправление — вложенный цикл. Для каждого разведения повторяйте запрос, пока не будет введена допустимая оценка или не придёт q. Это гарантирует один из двух исходов для каждого элемента: оценка записана либо выполнение немедленно остановлено.
series = ["1:16", "1:32", "1:64", "1:128", "1:256"]
permitted = ["+", "++-", "-", "+-", "-a", "A"]
ledger = {}
for ratio in series:
while ratio not in ledger:
mark = input("Enter score: ")
if mark in permitted:
ledger[ratio] = mark
elif mark == "q":
print("Done!")
exit()
else:
print("Not a valid score! Try again")
print(ledger)
Если вы не хотите завершать процесс целиком через exit(), используйте флаг: выйдите из внутреннего цикла, а затем остановите внешний.
series = ["1:16", "1:32", "1:64", "1:128", "1:256"]
permitted = ["+", "++-", "-", "+-", "-a", "A"]
ledger = {}
stop_now = False
for ratio in series:
while ratio not in ledger:
mark = input("Enter score: ")
if mark in permitted:
ledger[ratio] = mark
elif mark == "q":
print("Done!")
stop_now = True
break
else:
print("Not a valid score! Try again")
if stop_now:
break
print(ledger)
Почему это важно
Проверка ввода должна быть детерминированной: либо вы фиксируете значение для текущего элемента, либо явно останавливаетесь. Если позволять циклу продвигаться при ошибочном вводе, данные получаются непоследовательными или неполными, и их сложно интерпретировать. Небольшое структурное изменение — подцикл на элемент — приводит управление в соответствие с требованиями.
Выводы
Привязывайте проверку к обрабатываемому элементу. Используйте вложенный цикл, чтобы повторять запрос, пока ввод не будет допустим или пока не поступит команда выхода. Не прибегайте к обработке исключений там, где исключений не ожидается: это создаёт шум, не повышая корректность. Выберите ясную стратегию выхода: либо немедленно завершайте работу, либо ставьте флаг для аккуратного прекращения. С такими поправками каждое разведение гарантированно получает оценку, а при необходимости сессия останавливается по задумке.