Исключения в Python: понятное руководство по try/except/else/finally с примерами

Если вы искали «исключения в Python: try/except/else/finally», вы в нужном месте. Ниже — ясное и практичное руководство: от базового синтаксиса до лучших практик и создания собственных исключений.
Что такое исключение
Исключение — это событие (ошибка) во время выполнения программы. Когда что-то идёт не так, Python «выбрасывает» исключение; если его не перехватить, программа завершается с трассировкой.
print('Введите целое число:')
s = input()
try:
x = int(s)
print('Квадрат:', x * x)
except ValueError:
print('Это не похоже на целое число')
Базовый синтаксис try/except
Минимально: код в try, обработка конкретной ошибки в except. Ловите именно те исключения, которые ожидаете:
data = {'count': '10'}
try:
count = int(data['count'])
except KeyError:
print('Ключ count отсутствует')
except ValueError:
print('count есть, но значение не число')
Можно группировать несколько типов:
try:
result = 10 / int('0')
except (ZeroDivisionError, ValueError) as e:
print('Ошибка вычислений:', e)
Блоки else и finally
- else выполняется, если исключения не было.
- finally выполняется всегда (подходит для освобождения ресурсов).
f = None
try:
f = open('data.txt', 'r', encoding='utf-8')
content = f.read()
except FileNotFoundError:
print('Файл не найден')
else:
print('Символов в файле:', len(content))
finally:
if f is not None:
f.close()
Совет: для файлов чаще используют with, но в контексте изучения исключений полезно увидеть, как работает finally.
Как выбрасывать исключения: raise
Используйте raise, когда функция не может корректно продолжать работу:
def sqrt_str(s: str) -> float:
try:
x = float(s)
except ValueError as e:
raise ValueError(f'Нельзя преобразовать \"{s}\" к числу') from e
if x < 0:
raise ValueError('Число должно быть неотрицательным')
return x ** 0.5
print(sqrt_str('9')) # 3.0
print(sqrt_str('-1')) # ValueError
Обратите внимание на from e — это связывает исходную ошибку с новой (удобно в отладке).
Собственные классы исключений
Для библиотек и крупных проектов полезно определять свои исключения — это упрощает точечную обработку:
class ConfigError(Exception):
"""Базовая ошибка конфигурации приложения."""
class MissingKeyError(ConfigError):
pass
def load_port(cfg: dict) -> int:
try:
raw = cfg['PORT']
except KeyError as e:
raise MissingKeyError('В конфигурации нет ключа PORT') from e
try:
return int(raw)
except ValueError as e:
raise ConfigError(f'Некорректный PORT: {raw}') from e
Лучшие практики обработки ошибок
- Ловите только ожидаемые исключения. Избегайте bare except: без указания типа.
- Не перехватывайте BaseException (он включает SystemExit, KeyboardInterrupt). Если нужно «широко», используйте Exception.
- Не «проглатывайте» ошибки молча — минимум логируйте.
- Преобразуйте исключения на границах слоёв (например, низкоуровневые ошибки в доменные).
- Чаще следуйте принципу EAFP (Easier to Ask for Forgiveness than Permission) вместо LBYL.
# LBYL (проверяем заранее)
def get_user_age_lbyl(d):
if 'age' in d and isinstance(d['age'], int):
return d['age']
return 0
# EAFP (пытаемся и обрабатываем)
def get_user_age_eafp(d):
try:
return int(d['age'])
except (KeyError, ValueError, TypeError):
return 0
Оба подхода жизнеспособны, но EAFP часто короче и быстрее в типичном случае «всё хорошо», а LBYL полезен, когда проверки дешевле, чем обработка исключений.
Логирование вместо print
В продакшене используйте logging, чтобы не потерять важные детали:
import logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s %(message)s')
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
logging.exception('Деление на ноль для a=%s, b=%s', a, b)
return None
print(divide(10, 2))
print(divide(10, 0))
Частые ошибки разработчиков
- except: без типа — может скрыть критические сигналы (например, KeyboardInterrupt).
- Перехватывать Exception и ничего не делать. Минимум — логируйте или поднимайте дальше.
- Использовать assert для пользовательской валидации. Assert — инструмент отладки, может быть отключён флагом -O.
# Плохо: assert для бизнес-логики
def set_age(age):
assert age >= 0, 'Возраст должен быть неотрицательным' # может быть отключён
# Хорошо: явная проверка и исключение
def set_age_safe(age):
if age < 0:
raise ValueError('Возраст должен быть неотрицательным')
Небольшая практика: надёжный парсер целых
def parse_int(text: str) -> int:
text = text.strip()
if not text:
raise ValueError('Пустая строка не может быть числом')
try:
return int(text)
except ValueError as e:
# Добавим контекст к ошибке
raise ValueError(f'\"{text}\" — не целое число') from e
for s in ['42', ' 7 ', '', '3.14', 'abc']:
try:
print(s, '=>', parse_int(s))
except ValueError as err:
print('Ошибка:', err)
Когда использовать else и finally
- else: когда нужно выполнить шаг только при отсутствии ошибок (например, подтверждение или пост-обработка результата).
- finally: всегда освобождайте ресурсы (закрывайте файлы, соединения, блокировки).
Хотите системно прокачать основы Python (включая исключения) на практике? Посмотрите подробный и дружелюбный к новичкам курс «Программирование на Python с Нуля до Гуру» — стартуйте уверенно и без пробелов.
Итоги
Исключения в Python и блоки try/except/else/finally позволяют писать надёжный код: предсказуемо обрабатывать ошибки, добавлять контекст и безопасно освобождать ресурсы. Следуйте лучшим практикам, ловите только нужные ошибки, логируйте проблемы и создавайте собственные классы исключений для чистой архитектуры.
-
-
Михаил Русаков
Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.