Написать пост

Как использовать try — except и не испортить себе жизнь

Аватарка пользователя Елена Капаца

Разобрали на примерах, когда и как отлавливать ошибки с помощью блока и рассказали, когда эту конструкцию лучше не задействовать

Обложка поста Как использовать try — except и не испортить себе жизнь

Зачастую новичков в Python ошеломляет многочасовой дебаггинг простых, казалось бы, программ. На элементарные скрипты в 100 строк кода можно потратить несколько часов, ведь каждый трейсбэк кажется огромным и слишком насыщенным. Способность фильтровать такие данные без микрофрустраций формируется лишь спустя десятки часов отладки.

Справиться с негативом помогает конструкция try — except. В Python это лишь способ обработать ошибку и не «обронить» программу. Но если использовать ее не там и не так, лучше не станет (или вовсе будет хуже). Чтобы таких проблем не возникало, изучите этот гайд по применению обработчика.

Поначалу непросто запомнить, в каких ситуациях что возникает, а уже нужно вовсю обрабатывать исключения. Оттого люди разочаровываются в программировании: от джунов столь многое требуется в самом начале, причем неочевидно.

Вашему вниманию — мой личный топ самых частых исключений в дата-аналитике. Для каждой приведу примеры из своей практики.

ВРЕЗКА. Скажу честно, в первое время глаза разбегались при виде диаграммы исключений. Если хотите освежить в памяти полный перечень, обратитесь к статье.

Можно и нужно использовать try — except

KeyError

Если при чтении файла метод json.loads() возвращает вместо словаря список, то при попытке обратиться к ключу message можно схватить такое исключение:

			[
    {'id': 3721, 'date': '2023-01-30 02:45:37+00:00', 'message': "Java ...    News Roundup: JDK 20...'},
    {'id': 3721, 'date': '2023-01-30 02:45:37+00:00', 'message': "Java ...    News Roundup: JDK 20...'}
]
		
			>>> print(replicas['message'])...Traceback
...KeyError: 'message'
		

Эту ошибку легко допустить, когда не знаешь тип возвращаемого объекта.

Представьте, что мы выгружаем логи бота по дням, а в выходные не случилось ни одной беседы. Тогда программа вернет объект None («ничего») про воскресенье, и у него тоже нет ключа ‘message’. Здесь try — except играет важную роль: навесив такой блок, мы обработаем ситуацию с пустотами:

			try:
	# Обрабатываем логи и заливаем результат в хранилище
except KeyError:
	pass # Пропустим обработку пустого дня
		

IndexError

Допустим, мы и дальше получаем список реплик бота, причем каждый день длина этого перечня разная. Если все же приходится при создании цикла опираться на некую константу, которая с количеством реплик не всегда совпадает, try — except поможет справиться с ошибкой индекса:

			>>> replicas = [
>>>     {'id': 3721, 'date': '2023-01-30 02:45:37+00:00', 'message': "Как получить помощь?'},
>>>     {'id': 3721, 'date': '2023-01-30 02:45:37+00:00', 'message': "Сообщение оператора'}
>>> ]>>> >>> INEVITABLE_CONSTANT = 5 # Неизбежная константа
>>> i = 0>>> 
>>> while i <= INEVITABLE_CONSTANT:
>>>    print(replicas[i]) # Выведем реплики в консоль (а список длиной 2)
... 
... IndexError: list index out of range
		

Указываем во второй части конструкции, с какой ошибкой может столкнуться скрипт:

			try:
    while i <=  inevitable_constant:
    print(replicas[i]) # Выведем реплики в консоль (а список длиной 2)
except IndexError:
    pass
		

ВРЕЗКА. Это пример, за который коллеги поопытнее могут закидать меня камнями, ибо от таких констант вообще стоит избавляться. Но не всегда мы пишем код, который потом не нужно рефакторить.

TypeError + ValueError

Если вы, скажем, планируете оперировать тем, что ввел пользователь, то input() может сыграть с вами злую шутку:

			query = input("Введите число от 0 до 10: ")

y = query + 1
print(y)
		

По умолчанию метод возвращает строку, а мы-то предполагаем целочисленное значение:

			Traceback (most recent call last):
  File "/Users/elenakapatsa/Repositories/gpt-over-bpm-data/sample.py", line 3, in 
    y = query + 1
        ~~~~~~^~~
TypeError: can only concatenate str (not "int") to str
		

try, в принципе, может справиться с такой ситуацией, если вы понимаете типы данных для проводимых операций (в данном случае, сложения):

			query = input("Введите число от 0 до 10: ")

try:
    y = query + 1
    print(y)
except TypeError:
   query = int(query)
   y = query + 1
    print(y)
		

Бонусный прием

В PEP 654 были объявлены группы исключений (Exception Groups): они позволяют заложить в одну строку except сразу несколько типов ошибок:

			eg = ExceptionGroup(
     "one",
     [
         TypeError(1),
         ExceptionGroup(
             "two",
              [TypeError(2), ValueError(3)]
         ),
         ExceptionGroup(
              "three",
               [OSError(4)]
         )
     ]
)
		

Если исследовать типы ошибок нет времени, можно на скорую руку заложить в такую группу все популярные ошибки и обернуть блоком try — except всю программу:

			try:
	# Код
except eg:
	# Не делаем ничего, к примеру
		

Не стоит использовать try — except

AttributeError

Представим, что мы храним текстовые документы в специализированной базе: текст (text) и координаты его вектора в многомерном пространстве (values):

Как использовать try — except и не испортить себе жизнь 1

Библиотека langchain, когда запрашивает близкие по смыслу предложения, ищет близконаправленные вектора и возвращает список документов res в преобразованном виде:

			[
   (Document(page_content="значение не является значением…", metadata={'source': 'data/sample.md'}), 0.841107249),
   (Document(page_content="значение не является…", metadata={'source': 'data/sample.md'}), 0.838257194)
]
		

Если по квадратным скобкам узнать список в этом выводе можно, то определить, что каждый элемент — кортеж, уже сложнее. Блок try в таких ситуациях малоприменим, ибо тип данных задан автором библиотеки. Не ясно, какую ошибку класть в except.

Потому встроенная функция type() должна стать завсегдатаем в коде в первый год обучения:

			print(type(res[0]))
		

ImportError

Те из нас, кто привык работать в ноутбуках на базе Google Colaboratory / Jupyter Notebook, помнят, что в среду предустановлено множество популярных сторонних инструментов, и дополнительно их устанавливать командой pip не нужно.

При работе с классической Python-программой легко забыть, что тот или иной инструмент не подключен. Или, что еще хуже, для данной версии Python не предусмотрена конкретная версия библиотеки. Недавно при запуске скрипта с langchain на Python 3.9. я была вынуждена обновлять языковой пакет до 3.11.5, ибо нужная langchain==0.0.194 в 3.9 попросту недоступна.

Для таких ситуаций try — except малополезен. С подключаемыми инструментами все равно придется разбираться вручную. Если в вашей практике есть пример, когда ImportError удалось отхэндлить с помощью try, поделитесь в комментариях.

Прерывание с клавиатуры (KeyboardInterrupt)

В этом случае обработка try нужна, только если вы хотите замучать коллегу-джуна, который еще не знает, что прерывание комбинацией Ctrl + C можно тоже обработать при исполнении программы. В остальном этого исключения редко добиваются после деплоя.

Заключение

Давайте вкратце повторим, когда применять конструкцию try — except, а когда лучше избегать:

Как использовать try — except и не испортить себе жизнь 2
Следите за новыми постами
Следите за новыми постами по любимым темам
1К открытий2К показов