Исключения при вводе: как обрабатывать ошибки в Python

Раздел: Ошибки -> Исключения при вводе

Обработка ошибок ввода в Python

При взаимодействии с пользователем через функцию input() часто возникают ситуации, когда введённые данные невозможно преобразовать в ожидаемый тип. Например, при попытке преобразовать строку в целое число с помощью int() возникает исключение ValueError. Корректная обработка таких ошибок позволяет сделать программу устойчивой и понятной для пользователя.

Как эффективно обрабатывать ошибки ввода с помощью конструкции try-except?

Самое распространённое и надёжное решение - обернуть преобразование ввода в блок try-except с перехватом исключений типа ValueError. Пример для преобразования в целое число:

try:
    age = int(input("Введите ваш возраст: "))
except ValueError:
    print("Ошибка: необходимо ввести целое число.")

Python input error (обработка ошибок ввода в python)

Если пользователь вводит нечисловую строку, блок except перехватывает исключение и выводит сообщение. После этого программа продолжает выполнение, а переменная age остаётся неопределённой. Для продолжения работы требуется повторный запрос ввода, что реализуется через циклы (описано в других вариантах).

Типичные ошибки и способы их решения:

  • Забывают указать конкретный тип исключения (пишут просто except:), что перехватывает все исключения, включая SystemExit - это усложняет отладку. Решение: всегда указывать except ValueError: или другой конкретный тип.
  • Игнорируют возможные исключения при преобразовании в дробные числа - float() также выбрасывает ValueError. Решение: использовать универсальный обработчик с сообщением о необходимости ввести число.

Как проверить ввод на то, что строка состоит из цифр, с помощью isdigit?

Метод str.isdigit() возвращает True, если все символы строки являются цифрами. Этот способ подходит, когда требуется лишь целое положительное число.

user_input = input("Введите только цифры: ")
if user_input.isdigit():
    number = int(user_input)
    print(f"Вы ввели число {number}")
else:
    print("Ошибка: строка содержит нецифровые символы.")

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

Типичные проблемы:

  • Пустая строка: isdigit() вернёт False, но сообщение может ввести в заблуждение. Решение: предварительно проверять, что строка не пуста.
  • Числа с ведущими нулями (например, "007") считаются цифрами, но при преобразовании в int теряют нули - это не всегда ошибка.

Как обработать несколько типов исключений одновременно?

Иногда ввод может вызвать не только ValueError, но и TypeError (если, например, по ошибке передаётся не строка). Допускается указывать несколько типов исключений в кортеже.

try:
    value = int(input("Введите число: "))
except (ValueError, TypeError) as exc:
    print(f"Ошибка ввода: {exc}")

Переменная exc содержит текст сообщения об ошибке, что полезно для диагностики.

Типичная ошибка:

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

Как организовать повторный запрос ввода при ошибке с использованием рекурсии?

Рекурсивная функция вызывает саму себя при возникновении исключения, пока не будет получен корректный ввод.

def get_number():
    try:
        return int(input("Введите число: "))
    except ValueError:
        print("Неверный ввод, попробуйте ещё раз.")
        return get_number()  # рекурсивный вызов

number = get_number()

Этот способ элегантен, но при большом количестве ошибок может привести к переполнению стека. Глубина рекурсии в Python ограничена (обычно около 1000).

Проблемы:

  • Рекурсия не подходит для сценариев с очень частыми ошибками или для бесконечных циклов.
  • Стек вызовов растёт с каждой ошибкой, что может вызвать RecursionError. Альтернатива - цикл while.

Как использовать цикл while для повторного ввода до получения корректного значения?

Цикл while True с break при успешном преобразовании - самый распространённый и безопасный способ организации повторного запроса.

while True:
    try:
        age = int(input("Введите возраст: "))
        break
    except ValueError:
        print("Пожалуйста, введите целое число.")
print(f"Ваш возраст: {age}")

После успешного ввода цикл прерывается, и программа продолжает выполнение. Такой подход не накапливает стек и работает неограниченно долго.

Типичные ошибки:

  • Забывают поставить break - цикл становится бесконечным.
  • Не предусматривают выход по условию (например, пустая строка для завершения). Решение: добавить проверку на пустой ввод.

Как использовать assert для проверки ввода в отладочных целях?

assert выбрасывает исключение AssertionError, если условие ложно. Этот метод подходит для отладки и для случаев, когда ожидается строгий формат ввода.

try:
    user_input = input("Введите yes или no: ")
    assert user_input.lower() in ("yes", "no"), "Допустимы только yes или no"
except AssertionError as err:
    print(f"Ошибка: {err}")
else:
    print("Ввод корректен.")

Однако assert можно отключить флагом -O, поэтому для production-кода он не рекомендуется.

Проблемы:

  • Ассерты могут быть отключены, что приведёт к необработанным ошибкам.
  • Назначение assert - проверка инвариантов, а не обработка пользовательского ввода.

Как применить регулярные выражения для проверки сложного шаблона ввода?

Модуль re позволяет задать точный формат строки, например, дату в формате ДД.ММ.ГГГГ или номер телефона. После проверки соответствия шаблону можно выполнять преобразование с уверенностью.

import re

pattern = r"^\d{2}\.\d{2}\.\d{4}$"
user_input = input("Введите дату в формате ДД.ММ.ГГГГ: ")
if re.match(pattern, user_input):
    day, month, year = map(int, user_input.split('.'))
    print(f"День: {day}, Месяц: {month}, Год: {year}")
else:
    print("Неверный формат даты.")

Этот метод полезен, когда ввод должен строго соответствовать определённому формату (имя файла, номер заказа).

Типичные ошибки:

  • Неправильное экранирование символов в регулярном выражении (например, точка вместо \.). Решение: использовать сырые строки r"...".
  • Проверка через re.match даёт совпадение только в начале строки; для точного совпадения добавляют ^ и $.

Как создать пользовательскую функцию для безопасного ввода с указанием типа и ограничений?

Целесообразно написать универсальную функцию, которая запрашивает ввод, преобразует его в заданный тип и проверяет дополнительные условия, повторяя запрос при ошибке.

def safe_input(prompt, cast_func, valid_range=None, err_msg="Неверный ввод"):
    while True:
        try:
            value = cast_func(input(prompt))
            if valid_range is not None and value not in valid_range:
                print("Значение вне допустимого диапазона.")
                continue
            return value
        except ValueError:
            print(err_msg)

# Пример использования:
age = safe_input("Введите возраст: ", int, range(0, 150), "Возраст должен быть целым числом от 0 до 149.")

Такая функция сокращает дублирование кода и делает программу модульной. Можно передавать любую функцию преобразования (float, complex) и любые проверки.

Проблемы:

  • Сложность в реализации для нестандартных типов (например, ввод даты). Решение: передать кастомную функцию cast_func.
  • При использовании valid_range как диапазона целых чисел range проверяет только целые числа; для float нужна отдельная проверка.

Расширенные примеры обработки ошибок ввода

Пример 1: Обработка ввода с проверкой допустимого диапазона значений

При вводе возраста или температуры часто требуется не только числовой формат, но и попадание в заданный интервал. Ниже приведён код, который запрашивает целое число и проверяет, что оно находится в пределах от 1 до 100.

Пример
while True:
    try:
        value = int(input("Введите число от 1 до 100: "))
        if 1 <= value <= 100:
            print(f"Принято: {value}")
            break
        else:
            print("Число вне диапазона 1-100. Повторите ввод.")
    except ValueError:
        print("Ошибка: требуется целое число.")
Введите число от 1 до 100: сто
Ошибка: требуется целое число.
Введите число от 1 до 100: 101
Число вне диапазона 1-100. Повторите ввод.
Введите число от 1 до 100: 50
Принято: 50

В коде try-except перехватывает ValueError, а дополнительное условие if отклоняет значения за пределами интервала. Вывод демонстрирует реакции на разные ошибки.

Пример 2: Ввод нескольких чисел через пробел с обработкой ошибок

Зачастую требуется ввести последовательность чисел в одной строке. Проблема в том, что часть элементов может быть нечисловой. Для устойчивости применяется поэлементная проверка.

Пример
def parse_numbers(input_str):
    parts = input_str.split()
    numbers = []
    for item in parts:
        try:
            num = float(item)
            numbers.append(num)
        except ValueError:
            print(f"Предупреждение: '{item}' не является числом, пропускаем.")
    return numbers

user_input = input("Введите числа через пробел: ")
result = parse_numbers(user_input)
print("Распознанные числа:", result)
Введите числа через пробел: 10 20 abc 30.5 4e2 0,5
Предупреждение: 'abc' не является числом, пропускаем.
Предупреждение: '0,5' не является числом, пропускаем.
Распознанные числа: [10.0, 20.0, 30.5, 400.0]

Обработка ошибок на уровне отдельного элемента позволяет сохранить корректные значения. В примере использован float(), чтобы поддерживать как целые, так и дробные числа. Десятичная запятая (0,5) не является допустимым литералом в Python, поэтому такая строка отвергается.

Пример 3: Обработка ошибок ввода при использовании argparse

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

Пример
import argparse
import sys

def parse_args():
    parser = argparse.ArgumentParser(description="Пример обработки ошибок ввода.")
    parser.add_argument('--count', type=int, help='Количество элементов')
    try:
        args = parser.parse_args()
        return args
    except SystemExit:
        # argparse вызывает sys.exit при ошибке
        print("Некорректные аргументы. Использование: --count ЧИСЛО")
        sys.exit(1)

args = parse_args()
if args.count is not None:
    print(f"Запрошено {args.count} элементов.")
$ python script.py --count сто
usage: script.py [-h] [--count COUNT]
script.py: error: argument --count: invalid int value: 'сто'
Некорректные аргументы. Использование: --count ЧИСЛО

Исключение SystemExit (возникает при sys.exit) перехватывается, и выводится понятное сообщение. Это позволяет избежать стандартного технического трейсбека.

Пример 4: Обработка ввода из файла построчно с ошибками формата

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

Пример
def read_numbers(filename):
    numbers = []
    with open(filename, 'r') as file:
        for line_number, line in enumerate(file, 1):
            stripped = line.strip()
            if not stripped:
                continue
            try:
                number = float(stripped)
                numbers.append(number)
            except ValueError:
                print(f"Строка {line_number}: '{stripped}' не является числом (пропущено).")
    return numbers

# Пример содержимого файла data.txt:
# 10
# двадцать
# 30.5
# 1e2
# 0x10

result = read_numbers('data.txt')
print("Считанные числа:", result)
Строка 2: 'двадцать' не является числом (пропущено).
Строка 5: '0x10' не является числом (пропущено).
Считанные числа: [10.0, 30.5, 100.0]

Каждая строка проверяется отдельно. Некорректные строки не прерывают выполнение. Шестнадцатеричный литерал (0x10) не распознаётся функцией float(), поэтому он также пропускается. Для поддержки таких форматов требуется дополнительная логика или использование int() с основанием.

Обработка ошибок ввода в Python - comments

En
Python input error (python)