Как правильно обрабатывать ошибки в Python: от try-except-finally до ловли всех исключений

онлайн тренажер по питону
Онлайн-тренажер Python 3 для начинающих

Теория без воды. Задачи с автоматической проверкой. Подсказки на русском языке. Работает в любом современном браузере.

начать бесплатно

Обработка исключений в Python: Как работает try-except-finally и как обрабатывать несколько исключений?

Ошибки — неотъемлемая часть процесса программирования. Даже если ваш код написан идеально, всегда есть вероятность возникновения непредвиденных ситуаций: файл не найден, деление на ноль, недоступность сети и многое другое. Чтобы программа не завершалась аварийно при возникновении таких ситуаций, в Python предусмотрен мощный механизм обработки исключений с помощью конструкций try-except-finally.


Как работает try-except-finally в Python?

Конструкция try-except-finally позволяет перехватывать ошибки во время выполнения программы и корректно на них реагировать.

📌 Общий синтаксис:

python
try: # Код, который может вызвать исключение except SomeException: # Код обработки исключения finally: # Код, который выполнится в любом случае

📚 Простое объяснение работы блоков:

  • try — здесь размещается основной код, который может вызвать ошибку.

  • except — здесь описывается, что делать, если возникла ошибка.

  • finally — выполняется всегда, независимо от того, возникла ошибка или нет. Обычно используется для освобождения ресурсов (например, закрытия файлов или сетевых соединений).


Пример 1: Использование try-except-finally

python
try: number = int(input("Введите число: ")) result = 10 / number print(f"Результат: {result}") except ZeroDivisionError: print("Ошибка: Деление на ноль невозможно.") except ValueError: print("Ошибка: Введите корректное число.") finally: print("Завершение работы программы.")

Как работает этот код:

  1. Пользователь вводит число.

  2. Если введён 0 — сработает блок except ZeroDivisionError.

  3. Если введено не число — сработает except ValueError.

  4. В любом случае, блок finally выполнится последним.


📌 Зачем использовать finally?

  • Закрытие файлов:

python
file = open("data.txt", "r") try: content = file.read() finally: file.close() # Гарантированное закрытие файла
  • Освобождение ресурсов (сетевых соединений, баз данных).

  • Завершение логирования или очистка временных данных.


Как обрабатывать несколько исключений в одном блоке?

Иногда в одном блоке try могут возникать разные виды ошибок. В таких случаях Python позволяет использовать несколько блоков except или обрабатывать сразу несколько исключений в одном блоке.


📚 Способ 1: Использование нескольких блоков except

python
try: number = int(input("Введите число: ")) result = 100 / number print(result) except ZeroDivisionError: print("Нельзя делить на ноль!") except ValueError: print("Вы ввели не число!")

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


📚 Способ 2: Обработка нескольких исключений в одном блоке

Если для разных исключений требуется одинаковая реакция, их можно объединить:

python
try: number = int(input("Введите число: ")) result = 100 / number print(result) except (ZeroDivisionError, ValueError): print("Ошибка: Неправильный ввод или деление на ноль.")

Это удобно, если не нужно писать разный код обработки для разных типов ошибок.


📚 Способ 3: Использование общего обработчика исключений

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

python
try: risky_operation() except Exception as e: print(f"Произошла ошибка: {e}")

📌 Как получить информацию об исключении?

Иногда важно узнать подробности об ошибке. Для этого используется ключевое слово as.

python
try: 1 / 0 except ZeroDivisionError as e: print(f"Детали ошибки: {e}")

Вывод:

csharp
Детали ошибки: division by zero

Рекомендации по обработке исключений:

  1. Перехватывайте только те ошибки, которые ожидаете.
    Не стоит использовать общий перехват except Exception без крайней необходимости.

  2. Используйте finally для освобождения ресурсов.
    Например, закрывайте файлы и сетевые соединения.

  3. Не оставляйте пустые блоки except.
    Обязательно логируйте ошибки или информируйте пользователя.

python
except Exception: pass # Плохо! Ошибка будет проигнорирована

📚 Что происходит при отсутствии обработки исключения?

Если исключение не перехвачено, программа завершится с ошибкой, и в консоли появится Traceback с подробным описанием ошибки и указанием строки, где она возникла.

 

Продвинутые приёмы обработки ошибок и работы с аргументами по умолчанию в Python

Помимо стандартных ошибок и исключений, в Python существует ряд менее очевидных ловушек, с которыми часто сталкиваются даже опытные разработчики. В этой части статьи мы разберём, почему использование изменяемых (mutable) объектов в качестве аргументов по умолчанию может привести к неожиданным ошибкам, а также как правильно перехватывать все исключения, не скрывая при этом реальные проблемы в программе.


Почему mutable объекты опасны как аргументы по умолчанию?

Python интерпретирует значения аргументов по умолчанию один раз — при определении функции, а не при каждом вызове. Это может приводить к неожиданному поведению, если в качестве такого аргумента используется изменяемый объект, например, список, словарь или множество.


📚 Пример проблемы:

python
def append_to_list(value, lst=[]): lst.append(value) return lst print(append_to_list(1)) # [1] print(append_to_list(2)) # [1, 2] — а ожидали [2]!

Почему так происходит?
При первом вызове функции создаётся объект lst, который сохраняется в памяти и используется во всех последующих вызовах, если не передано новое значение аргумента. В результате данные накапливаются в одном и том же списке, что редко является желаемым поведением.


Как избежать этой ошибки?

Используйте значение None в качестве аргумента по умолчанию и создавайте новый объект внутри функции:

python
def append_to_list(value, lst=None): if lst is None: lst = [] lst.append(value) return lst print(append_to_list(1)) # [1] print(append_to_list(2)) # [2] — как и ожидалось

📌 Какие объекты считаются изменяемыми (mutable)?

  • Списки (list)

  • Словари (dict)

  • Множества (set)

  • Пользовательские объекты (если их состояние может изменяться)

Неизменяемые объекты (immutable):

  • Числа (int, float)

  • Строки (str)

  • Кортежи (tuple)

  • Булевы значения (True, False)

Использование изменяемых объектов по умолчанию в аргументах функций — классический антипаттерн, который может приводить к трудноуловимым багам, особенно в больших проектах.


Как ловить все исключения, но не скрывать ошибки?

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


📚 Как ловить все исключения корректно?

Используйте базовый класс Exception для перехвата всех стандартных исключений и обязательно логируйте или выводите информацию о пойманной ошибке.

python
try: # Рискованный код result = 10 / 0 except Exception as e: print(f"Произошла ошибка: {e}")

Это даст следующий вывод:

csharp
Произошла ошибка: division by zero

📌 Почему нельзя писать просто except: без указания типа исключения?

python
try: risky_operation() except: pass # ПЛОХО! Ошибки скрыты, программа молча продолжит выполнение

Такой подход полностью подавляет все исключения, включая системные (например, KeyboardInterrupt и SystemExit), что делает отладку практически невозможной.


Правильный способ ловить все исключения, но не скрывать ошибки:

  1. Перехватывайте только наследников Exception, а не все возможные исключения.

python
try: perform_task() except Exception as e: print(f"Ошибка: {e}") raise # Повторно выбрасываем исключение, чтобы не терять его
  1. Используйте модули логирования для записи ошибок вместо простого print().

python
import logging logging.basicConfig(level=logging.ERROR) try: perform_task() except Exception as e: logging.error("Произошла ошибка", exc_info=True)

Такой подход позволяет сохранить полный Traceback в логах.

  1. В некоторых случаях удобно использовать конструкцию try-except-else-finally:

python
try: print("Попытка выполнить код...") result = 100 / 2 except Exception as e: print(f"Ошибка: {e}") else: print("Код выполнился без ошибок.") finally: print("Завершение программы.")

📚 Какие исключения не стоит перехватывать без крайней необходимости?

  • KeyboardInterrupt — пользователь нажал Ctrl+C, программа должна корректно завершиться.

  • SystemExit — вызов функции sys.exit().

  • MemoryError — нехватка памяти.

  • GeneratorExit — закрытие генератора.

Такие исключения лучше не перехватывать глобально, чтобы программа могла корректно завершаться в критических ситуациях.


Вывод

  • Не используйте изменяемые объекты как аргументы по умолчанию в функциях. Это одна из самых коварных ошибок в Python. Правильное решение — использовать None и создавать объект внутри функции при необходимости.

  • Перехватывайте только те исключения, которые вы реально можете обработать.

  • Всегда информируйте пользователя или логируйте ошибки, если они возникают.

  • Для качественного отслеживания проблем в больших проектах используйте модуль logging вместо простого вывода в консоль.

Грамотная обработка ошибок делает ваш код надёжнее, а программы — стабильнее и профессиональнее.

Новости