try/except в Python — обработка ошибок для аналитика
Коротко
try/except — механизм обработки ошибок (исключений) в Python. Вместо того чтобы программа падала при ошибке, вы перехватываете её и решаете, что делать дальше. Аналитик сталкивается с этим постоянно: файл не найден, деление на ноль в метрике, битая строка в CSV. На собеседованиях спрашивают про разницу except Exception и голого except, про finally и подход EAFP.
Базовый синтаксис
try:
result = 10 / 0
except ZeroDivisionError:
result = None
print('Деление на ноль')Код внутри try выполняется. Если возникает исключение указанного типа — управление переходит в except. Если ошибки нет — except пропускается.
Перехват конкретных исключений
Всегда ловите конкретный тип ошибки, а не все подряд:
config = {'host': 'localhost', 'port': 5432}
try:
password = config['password']
except KeyError:
password = 'default'
try:
value = int('abc')
except ValueError:
value = 0Несколько блоков except
def safe_read_metric(filepath):
try:
with open(filepath) as f:
return float(f.read().strip())
except FileNotFoundError:
print(f'Файл {filepath} не найден')
return None
except ValueError:
print('Не удалось преобразовать в число')
return NoneМожно объединить типы в один блок: except (ValueError, TypeError):.
Основные типы исключений
| Исключение | Когда возникает |
|---|---|
ValueError |
Неверное значение: int('abc') |
KeyError |
Ключ отсутствует в словаре |
TypeError |
Неверный тип: '2' + 2 |
IndexError |
Индекс за пределами списка |
FileNotFoundError |
Файл не найден при чтении |
ZeroDivisionError |
Деление на ноль |
AttributeError |
Атрибут или метод не существует |
ImportError |
Модуль не удалось импортировать |
else и finally
try:
data = load_report('report.csv')
except FileNotFoundError:
print('Отчёт не найден')
else:
# Выполняется, только если ошибки НЕ было
print(f'Загружено {len(data)} строк')
finally:
# Выполняется ВСЕГДА — и при ошибке, и без
print('Завершение обработки')finally полезен для освобождения ресурсов: закрытие соединения, удаление временного файла. Он выполнится даже если внутри try стоит return.
raise — выбрасывание исключений
def conversion_rate(purchases, visits):
if visits == 0:
raise ValueError('visits не может быть 0')
return purchases / visits
# Перевыброс — ловим, логируем, бросаем дальше
try:
rate = conversion_rate(10, 0)
except ValueError as e:
print(f'Ошибка: {e}')
raise # пробросить исключение вышеraise без аргументов пробрасывает текущее исключение — это стандартный паттерн для логирования с сохранением traceback.
Примеры для аналитика
Безопасное деление метрик
def safe_divide(a, b, default=0.0):
try:
return a / b
except ZeroDivisionError:
return default
cr = safe_divide(purchases, visits)
arpu = safe_divide(revenue, users)Обработка пропущенных ключей
def get_segment_metric(report, segment, metric):
try:
return report[segment][metric]
except KeyError:
return None
# Вместо цепочки if segment in report and metric in report[segment]
value = get_segment_metric(report, 'organic', 'revenue')Парсинг дат
from datetime import datetime
def parse_date(s):
for fmt in ('%Y-%m-%d', '%d.%m.%Y', '%d/%m/%Y'):
try:
return datetime.strptime(s, fmt)
except ValueError:
continue
raise ValueError(f'Неизвестный формат даты: {s}')EAFP vs LBYL
Два подхода к обработке ошибок:
- LBYL (Look Before You Leap) — проверить перед действием:
if key in d: d[key] - EAFP (Easier to Ask Forgiveness than Permission) — сделать и поймать ошибку:
try: d[key] except KeyError:
Python поощряет EAFP. Это быстрее при частых успехах (нет двойной проверки) и защищает от race conditions. На собеседованиях полезно знать оба термина и уметь объяснить разницу.
Частые ошибки
Голый except: без типа. Ловит вообще всё, включая KeyboardInterrupt и SystemExit. Программу нельзя остановить через Ctrl+C. Минимум используйте except Exception:.
# Плохо
try:
process_data()
except:
pass
# Нормально
try:
process_data()
except Exception as e:
logging.error(f'Ошибка обработки: {e}')Глушение ошибок через pass. Ошибка произошла, но вы о ней не узнали — данные молча испорчены. Всегда логируйте или хотя бы возвращайте дефолтное значение осмысленно.
Слишком широкий try-блок. Оборачивайте минимум кода — только ту строку, которая может упасть. Иначе поймаете не ту ошибку и потратите часы на отладку.
Вопросы с собеседований
— Чем except Exception отличается от голого except?
— except Exception ловит все обычные ошибки, но пропускает SystemExit, KeyboardInterrupt и GeneratorExit — они наследуются от BaseException, а не от Exception. Голый except: ловит вообще всё, что мешает корректному завершению программы.
— Когда выполняется блок finally?
— Всегда: и при ошибке, и без, и даже если внутри try стоит return. Единственное исключение — os._exit() или аварийное завершение процесса.
— Что такое EAFP? Приведите пример.
— Easier to Ask Forgiveness than Permission — pythonic-подход, при котором сначала выполняют действие, а ошибку ловят через try/except. Противоположность — LBYL (проверка перед действием через if). Пример: try: value = d[key] вместо if key in d: value = d[key].
— Зачем нужен else в try/except/else?
— Код в else выполняется, только если try завершился без ошибок. Это позволяет отделить «рискованный» код от кода, который должен выполняться только при успехе, и не ловить лишние исключения.
— Можно ли создать своё исключение?
— Да, нужно унаследоваться от Exception: class InsufficientDataError(Exception): pass. Кастомные исключения полезны в библиотеках и крупных проектах для различения типов ошибок.
Потренировать Python-вопросы на практике можно в тренажёре Карьерника. Подробнее про словари и чтение CSV через pandas. Больше примеров вопросов — на отдельной странице.
Открыть тренажёр в Telegram — вопросы по Python, pandas, SQL и аналитике. Бесплатно.
FAQ
Нужно ли оборачивать весь код в try/except?
Нет. Оборачивайте только те участки, где ошибка ожидаема и вы знаете, как её обработать: чтение файлов, парсинг данных, обращение к API. Необработанное исключение с traceback — это нормально на этапе разработки, оно показывает реальную проблему.
Влияет ли try/except на производительность?
Если ошибки не возникает — почти не влияет. Вход в try-блок практически бесплатный. Замедление происходит только при реальном выбросе исключения. Поэтому EAFP эффективен, когда ошибки — редкость.
Чем try/except отличается от if/else?
if/else — проверка условий. try/except — обработка ошибок, которые возникают во время выполнения. Используйте if для штатной логики ветвления, try/except — для ситуаций, когда что-то может пойти не так (файл пропал, сервер не ответил, формат данных неожиданный).
Как логировать исключения?
Используйте модуль logging с методом exception() — он автоматически добавляет traceback: except Exception as e: logging.exception('Ошибка'). В скриптах для анализа данных достаточно print, но в продакшене всегда используйте logging.