try/except в Python: шпаргалка

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Базовый синтаксис

try:
    # код, который может упасть
    risky_operation()
except SomeError:
    # что делать при ошибке
    handle_error()

Если в try возникло SomeError — управление передаётся в except. Если нет ошибки — except пропускается.

1. Самый частый паттерн

try:
    value = int(input("Введите число: "))
except ValueError:
    print("Это не число")

2. Несколько типов ошибок

try:
    result = data[key] / value
except KeyError:
    print("Нет такого ключа")
except ZeroDivisionError:
    print("Деление на ноль")

Или одной строкой:

try:
    ...
except (KeyError, ZeroDivisionError):
    print("Ошибка доступа или деления")

3. Получить объект исключения

try:
    risky_call()
except ValueError as e:
    print(f"Ошибка: {e}")
    print(f"Тип: {type(e).__name__}")

4. finally — выполняется всегда

try:
    file = open('data.csv')
    process(file)
except FileNotFoundError:
    print("Файла нет")
finally:
    file.close()  # выполнится и при успехе, и при ошибке

5. else — выполняется если ошибки не было

try:
    result = compute()
except ValueError:
    print("Ошибка")
else:
    print("Всё ок, result =", result)

else выполняется только если в try не было исключения. Удобно отделить «обработку ошибки» от «что делать дальше».

6. Перевыбросить ошибку дальше

try:
    risky_call()
except ValueError:
    log_error("ValueError в risky_call")
    raise  # пробросить ту же ошибку дальше

Или выбросить другую:

try:
    risky_call()
except ValueError as e:
    raise RuntimeError(f"Не получилось: {e}") from e

from e сохраняет оригинальный traceback.

7. Пустой except — плохой стиль

# плохо — ловит вообще всё, включая KeyboardInterrupt
try:
    ...
except:
    pass

# только немного лучше
try:
    ...
except Exception:
    pass

# хорошо — ловить конкретные типы
try:
    ...
except (ValueError, TypeError) as e:
    logger.error(e)

8. try/except в функции

def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None

safe_divide(10, 0)  # None
safe_divide(10, 2)  # 5.0

9. try/except в pandas

df = pd.read_csv('data.csv')

def safe_int(x):
    try:
        return int(x)
    except (ValueError, TypeError):
        return None

df['amount_int'] = df['amount'].apply(safe_int)

В pandas часто лучше использовать pd.to_numeric(errors='coerce'):

df['amount_int'] = pd.to_numeric(df['amount'], errors='coerce')

10. Контекстный менеджер (with) вместо try/finally

# старый способ
try:
    f = open('data.csv')
    data = f.read()
finally:
    f.close()

# правильный способ
with open('data.csv') as f:
    data = f.read()
# файл закроется автоматически

11. Логирование ошибок

import logging

try:
    risky_call()
except Exception as e:
    logging.exception("Ошибка в risky_call")
    # logging.exception автоматически добавляет traceback

12. Кастомное исключение

class DataValidationError(Exception):
    """Ошибка валидации данных"""
    pass

def validate(df):
    if df.empty:
        raise DataValidationError("Пустой DataFrame")
    if df['amount'].isnull().any():
        raise DataValidationError("NULL в amount")

try:
    validate(df)
except DataValidationError as e:
    print(f"Данные невалидны: {e}")

13. Walrus + try в list comprehensions (не работает)

В list comp нельзя try/except. Нужна функция:

# нельзя
result = [int(x) try except None for x in values]  # SyntaxError

# правильно — отдельная функция
def safe_int(x):
    try: return int(x)
    except: return None

result = [safe_int(x) for x in values]

Типичные исключения

Исключение Когда возникает
ValueError Некорректное значение (int("abc"))
TypeError Неверный тип ("a" + 1)
KeyError Нет ключа в dict
IndexError Индекс за пределами list
AttributeError Нет атрибута у объекта
FileNotFoundError Файл не существует
ZeroDivisionError Деление на 0
StopIteration Итератор закончился
RuntimeError Общая runtime ошибка
Exception Базовый класс всех

EAFP vs LBYL

Два подхода:

# LBYL — Look Before You Leap
if key in d:
    value = d[key]

# EAFP — Easier to Ask Forgiveness than Permission
try:
    value = d[key]
except KeyError:
    value = None

В Python обычно предпочитается EAFP — короче и часто быстрее (меньше двойных lookup).

Частые ошибки

Ошибка 1. Слишком широкий except

# плохо
try:
    ...
except Exception:
    pass

# хорошо — явный тип
except (ValueError, KeyError) as e:
    handle(e)

Ошибка 2. Ловить и молча игнорировать

# плохо
try:
    ...
except:
    pass  # ошибку никто не увидит

# хоть логировать
try:
    ...
except Exception as e:
    logger.warning(f"Ошибка: {e}")

Ошибка 3. try вокруг огромного блока

# плохо — непонятно, где упадёт
try:
    x = parse(data)
    y = transform(x)
    z = save(y)
    notify(z)
except Exception:
    handle()

# лучше — обернуть конкретную опасную операцию
x = parse(data)  # может упасть отдельно
try:
    z = save(y)
except DBError:
    retry_save(y)

Ошибка 4. except перед raise без сохранения цепочки

# плохо — теряем оригинальный traceback
try:
    ...
except ValueError:
    raise RuntimeError("ошибка")

# правильно
except ValueError as e:
    raise RuntimeError("ошибка") from e

Связанные темы

FAQ

Что быстрее: try/except или if?

Зависит. EAFP (try/except) быстрее, если ошибка редкая. LBYL (if) быстрее, если ошибка частая.

Когда использовать finally?

Для ресурсов, которые нужно освобождать (файлы, подключения). Но обычно with справляется элегантнее.

Чем отличается Exception от BaseException?

BaseException — корневой класс (включая KeyboardInterrupt, SystemExit). Exception — для «обычных» ошибок. Ловите Exception, не трогайте BaseException.

Как создать своё исключение?

Наследуйтесь от Exception или её подкласса:

class MyError(Exception):
    pass

Тренируйте Python — откройте тренажёр с 1500+ вопросами для собесов аналитиков.