Зачем нужна обработка ошибок?
Во время выполнения программы могут возникать ошибки (исключения): деление на ноль, неверный тип данных, отсутствующий файл. Если не обработать исключение — программа аварийно завершится.
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:без крайней необходимости.
✏️ Упражнение: Безопасный ввод
Напишите код, который запрашивает у пользователя два числа и выводит результат их деления. Обработайте ситуации:
- Пользователь ввёл не число (
ValueError) - Деление на ноль (
ZeroDivisionError)
# ВАШ КОД — напишите функцию safe_calculator() def safe_calculator(): try: a = float(input("Первое число: ")) b = float(input("Второе число: ")) # Ваш код здесь... except ValueError: print("Ошибка: нужно ввести число!") except ZeroDivisionError: print("Ошибка: на ноль делить нельзя!") safe_calculator() ▶ Запустить ↺ Сброс