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

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

Исключения в 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 позволяют писать надёжный код: предсказуемо обрабатывать ошибки, добавлять контекст и безопасно освобождать ресурсы. Следуйте лучшим практикам, ловите только нужные ошибки, логируйте проблемы и создавайте собственные классы исключений для чистой архитектуры.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий