Обработка исключений в Python
Программа спрашивает у пользователя возраст и прибавляет год:
Python 3.13age = int(input("Сколько вам лет? ")) print("Через год будет", age + 1)
Пока вводят число, всё работает. Но стоит пользователю набрать «сорок» вместо 40, и int() не сможет превратить это в число: программа аварийно завершится с ошибкой ValueError, а строка с print уже не выполнится. Один неверный ввод роняет всю программу.
Обработка исключений нужна как раз для таких моментов: перехватить ошибку, среагировать и продолжить работу, вместо того чтобы падать при первой же неожиданности. Мы дважды откладывали этот разговор — в уроках про текстовые файлы и инкапсуляцию. Пришло время.
Блок try-except
Рискованный код кладут в try, реакцию на ошибку — в except:
Python 3.13try: # код, который может упасть except ТипИсключения: # что делать, если упал
Вот как это выглядит на настоящей ошибке:
Python 3.13try: result = 10 / 0 except ZeroDivisionError: print("Ошибка: деление на ноль!")Ошибка: деление на ноль!
Программа не упала: Python дошёл до деления, увидел ошибку, перепрыгнул в except и продолжил работу.
Разные ошибки — разные реакции
В одном try может падать разное, и на каждый тип исключения заводится свой except. Python проверяет их по порядку и выполняет первый подходящий:
Python 3.13file_name = "data.txt" try: file = open(file_name, "r") number = int(file.readline().strip()) print(f"Результат: {100 / number}") except FileNotFoundError: print(f"Файл {file_name} не найден") except ValueError: print("В файле не число") except ZeroDivisionError: print("В файле ноль — делить нельзя")Файл data.txt не найден
Если реакция на разные ошибки одна и та же, типы перечисляют в скобках через запятую:
Python 3.13try: value = int("abc") except (ValueError, ZeroDivisionError): print("Произошла ошибка в вычислениях")Произошла ошибка в вычислениях
Блоки else и finally
else выполняется, только если в try ошибок не было:
Python 3.13try: number = int("42") except ValueError: print("Это не число") else: print(f"Успешно! Число: {number}")Успешно! Число: 42
finally выполняется всегда — была ошибка или нет:
Python 3.13f = open("example.txt", "w") try: f.write("Привет, мир!") finally: f.close() # выполнится, даже если запись упадёт print("Файл закрыт")Файл закрыт
Обратите внимание: open() стоит до try. Если поместить его внутрь и файл не откроется, переменной f просто не будет, и f.close() в finally сам упадёт. Именно эту возню автоматизирует оператор with из главы про файлы — он закрывает файл за вас.
В finally кладут то, что должно случиться в любом случае, — чаще всего освобождение ресурсов вроде открытых файлов.
Информация об ошибке: as
Через as можно получить сам объект исключения — например, чтобы показать его сообщение:
Python 3.13try: result = 10 / 0 except ZeroDivisionError as e: print(f"Сообщение: {e}")Сообщение: division by zero
Свои ошибки: raise
Оператор raise поднимает исключение вручную. Вы уже видели его в уроке про инкапсуляцию: там сеттер отказывался сохранять отрицательный баланс. Устроено это так:
Python 3.13def check_age(age): if age < 0: raise ValueError("Возраст не может быть отрицательным") print(f"Возраст {age} принят") try: check_age(-5) except ValueError as e: print(f"Ошибка: {e}")Ошибка: Возраст не может быть отрицательным
Не ловите всё подряд
Голый except: без типа перехватывает любые ошибки — в том числе те, о которых вы не подозревали, например опечатку в имени переменной. Программа «работает», а настоящая проблема молчит:
Python 3.13# Плохо: непонятно, что именно случилось try: number = int("abc") except: print("Какая-то ошибка")Какая-то ошибка# Хорошо: ловим ровно то, что ожидаем try: number = int("abc") except ValueError: print("Неверный формат числа")Неверный формат числа
Проверка понимания
Какой блок выполнится всегда, независимо от того, произошло исключение или нет?
В следующем уроке — декораторы. Вы уже встречали их дважды: @property в инкапсуляции и @abstractmethod в полиморфизме. Пришло время разобрать, как строка с @ устроена изнутри.
