Урок 11 Обработка исключений

try / except / finally / raise — как сделать программу устойчивой к ошибкам

⏱ ~10 мин чтения· 📘 Базовый

Зачем нужна обработка ошибок?

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

Python предоставляет механизм try/except для «перехвата» ошибок и безопасной реакции на них.

# Без обработки — программа упадёт
x = int(input("Введите число: "))   # Если ввести "abc" → ValueError
print(10 / x)                        # Если x = 0 → ZeroDivisionError

Синтаксис try/except

Опасный код помещается в блок try, а реакция на ошибку — в except:

try:
    x = int(input("Введите число: "))
    print("10 /", x, "=", 10 / x)
except ValueError:
    print("❌ Это не число!")
except ZeroDivisionError:
    print("❌ На ноль делить нельзя!")

⚠️ Важно: как только в блоке try возникает ошибка, выполнение сразу переходит в соответствующий except. Оставшаяся часть try не выполняется.

Несколько типов исключений в одном except

Можно перехватывать несколько типов ошибок в одном блоке:

try:
    x = int(input("Введите число: "))
    print(10 / x)
except (ValueError, ZeroDivisionError) as e:
    print(f"Ошибка: {e}")

Ключевое слово as позволяет получить сам объект исключения и вывести его описание.

Универсальный except

except: без указания типа (или except Exception:) перехватывает любую ошибку. Используйте осторожно:

try:
    risky_code()
except Exception as e:
    print(f"Что-то пошло не так: {e}")

⚠️ Важно: Совет: перехватывайте конкретные типы исключений. Голый except: может скрыть ошибки, которые вы не ожидали, и усложнить отладку.

Блок else

else выполняется, только если ошибок не было:

try:
    x = int(input("Число: "))
except ValueError:
    print("Это не число!")
else:
    print(f"Отлично! Вы ввели {x}")

Блок finally

finally выполняется всегда — была ошибка или нет. Используется для очистки ресурсов (закрытие файлов, соединений):

try:
    f = open("data.txt", "r")
    content = f.read()
except FileNotFoundError:
    print("Файл не найден!")
finally:
    f.close()        # Выполнится всегда
    print("Файл закрыт")

Полный порядок блоков

Стандартная структура: try → except → else → finally

try:
    # Код, который может вызвать ошибку
    result = 10 / int(input("Число: "))
except ValueError:
    # Если не число
    print("Нужно число!")
except ZeroDivisionError:
    # Если ноль
    print("Не дели на ноль!")
else:
    # Если ошибок НЕ было
    print("Результат:", result)
finally:
    # Выполнится ВСЕГДА
    print("Конец операции")

Оператор raise — создаём свои ошибки

raise позволяет вручную вызвать исключение. Полезно для проверки входных данных:

def set_age(age):
    if age < 0:
        raise ValueError("Возраст не может быть отрицательным!")
    if age > 150:
        raise ValueError("Слишком большой возраст!")
    print(f"Возраст: {age}")

try:
    set_age(-5)
except ValueError as e:
    print(e)   # "Возраст не может быть отрицательным!"

Создание своих типов исключений

Можно создать собственный класс исключения, унаследовав от Exception:

class NegativeAgeError(Exception):
    """Возраст не может быть отрицательным"""
    pass

def set_age(age):
    if age < 0:
        raise NegativeAgeError(f"Возраст {age} < 0!")
    print(f"OK: {age}")

try:
    set_age(-10)
except NegativeAgeError as e:
    print(f"Поймано своё исключение: {e}")

Основные встроенные исключения Python

ИсключениеКогда возникает
ValueErrorНеверное значение (например, int("abc"))
TypeErrorНеверный тип данных (например, "a" + 5)
IndexErrorИндекс за пределами списка/строки
KeyErrorОтсутствующий ключ в словаре
ZeroDivisionErrorДеление на ноль
FileNotFoundErrorФайл не найден
NameErrorИспользование необъявленной переменной
SyntaxErrorСинтаксическая ошибка в коде
ImportErrorНе удалось импортировать модуль
AttributeErrorОбращение к несуществующему атрибуту объекта

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

Классический паттерн — запрашивать ввод, пока пользователь не введёт корректное значение:

def get_number(prompt="Введите число: "):
    """Запрашивает число, пока не будет введено корректное"""
    while True:
        try:
  return int(input(prompt))
        except ValueError:
  print("❌ Это не число! Попробуйте ещё раз.")

age = get_number("Ваш возраст: ")
print(f"Ваш возраст: {age}")

Практический пример: безопасное деление

def safe_divide(a, b):
    """Безопасное деление с обработкой ошибок"""
    try:
        result = a / b
    except ZeroDivisionError:
        print(f"❌ Ошибка: деление {a} на 0 невозможно")
        return None
    except TypeError as e:
        print(f"❌ Ошибка типа: {e}")
        return None
    else:
        print(f"✅ {a} / {b} = {result:.2f}")
        return result

safe_divide(10, 2)    # ✅ 10 / 2 = 5.00
safe_divide(10, 0)    # ❌ Ошибка: деление 10 на 0 невозможно
safe_divide(10, "a")  # ❌ Ошибка типа: ...

Цепочка исключений (raise from)

Можно связать новое исключение с исходным:

try:
    x = int("abc")
except ValueError as original_error:
    raise RuntimeError("Не удалось обработать ввод") from original_error

Резюме

  • try — код, который может вызвать ошибку
  • except ТипОшибки — перехват конкретной ошибки
  • except (Тип1, Тип2) — перехват нескольких типов
  • as переменная — получить объект исключения
  • else — выполняется, если ошибок не было
  • finally — выполняется всегда (очистка ресурсов)
  • raise — вызвать исключение вручную
  • Свои исключения — наследование от Exception

⚠️ Важно: Золотое правило: перехватывайте только те исключения, которые можете осмысленно обработать. Не используйте голый except: без крайней необходимости.

✏️ Упражнение: Безопасный ввод

Напишите код, который запрашивает у пользователя два числа и выводит результат их деления. Обработайте ситуации:

  1. Пользователь ввёл не число (ValueError)
  2. Деление на ноль (ZeroDivisionError)

# ВАШ КОД — напишите функцию safe_calculator() def safe_calculator(): try: a = float(input("Первое число: ")) b = float(input("Второе число: ")) # Ваш код здесь... except ValueError: print("Ошибка: нужно ввести число!") except ZeroDivisionError: print("Ошибка: на ноль делить нельзя!") safe_calculator() ▶ Запустить ↺ Сброс