Блок finally в обработке исключений Python

19 июня 2026
Автор

Антон Ларичев

Введение

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

Именно для таких случаев Python предоставляет блок finally. В этой статье мы подробно разберём, что представляет собой finally, как его правильно использовать, какие подводные камни существуют и когда стоит предпочесть альтернативные подходы.

Что такое блок finally и зачем он нужен

Блок finally — это часть конструкции обработки исключений в Python, код внутри которой всегда выполняется, независимо от того:

  • Произошло ли исключение в блоке try
  • Было ли исключение поймано блоком except
  • Был ли выполнен оператор return, break или continue

Главная задача finally — гарантировать выполнение кода по завершении блока try, независимо от исхода. Это критически важно при работе с внешними ресурсами: файлами, сетевыми соединениями, соединениями с базами данных, блокировками и т.д.

Синтаксис: try / except / finally

Полная конструкция обработки исключений в Python выглядит следующим образом:

try:
    # Код, который может вызвать исключение
    результат = выполнить_операцию()
except ТипИсключения as e:
    # Код, который выполняется при возникновении исключения
    print(f"Ошибка: {e}")
finally:
    # Код, который выполняется ВСЕГДА — и при успехе, и при ошибке
    освободить_ресурсы()

Простой пример, демонстрирующий порядок выполнения:

def деление(a, b):
    try:
        # Пытаемся выполнить деление
        результат = a / b
        print(f"Результат: {результат}")
        return результат
    except ZeroDivisionError:
        # Перехватываем деление на ноль
        print("Ошибка: деление на ноль невозможно")
        return None
    finally:
        # Этот блок выполняется всегда
        print("Блок finally выполнен")

# Успешное выполнение
деление(10, 2)
# Вывод:
# Результат: 5.0
# Блок finally выполнен

# Выполнение с ошибкой
деление(10, 0)
# Вывод:
# Ошибка: деление на ноль невозможно
# Блок finally выполнен

Как видите, finally выполняется в обоих случаях — и при успешном делении, и при возникновении исключения.

Гарантированное выполнение при любых исключениях

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

def пример_без_перехвата():
    try:
        # Генерируем исключение типа ValueError
        raise ValueError("Что-то пошло не так")
    finally:
        # Этот блок выполнится даже без except
        print("Очистка в finally — выполнено")

try:
    пример_без_перехвата()
except ValueError as e:
    # Исключение поднялось сюда после выполнения finally
    print(f"Поймано исключение снаружи: {e}")

# Вывод:
# Очистка в finally — выполнено
# Поймано исключение снаружи: Что-то пошло не так

Это поведение гарантирует, что ресурсы будут освобождены даже при непредвиденных ошибках.

Если вы хотите детальнее изучить обработку исключений и другие основы Python — приходите на наш большой курс Основы Python. На курсе 209 уроков и 34 упражнения, AI-тренажёры для практики 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.

Использование finally для очистки ресурсов

Самый распространённый и практически значимый сценарий применения finally — это гарантированное освобождение ресурсов.

Работа с файлами

def прочитать_файл(путь_к_файлу):
    файл = None
    try:
        # Открываем файл
        файл = open(путь_к_файлу, 'r', encoding='utf-8')
        содержимое = файл.read()
        print(f"Файл прочитан, символов: {len(содержимое)}")
        return содержимое
    except FileNotFoundError:
        # Файл не найден
        print(f"Ошибка: файл '{путь_к_файлу}' не найден")
        return None
    except PermissionError:
        # Нет прав доступа
        print(f"Ошибка: нет прав для чтения файла '{путь_к_файлу}'")
        return None
    finally:
        # Закрываем файл, если он был открыт
        if файл is not None:
            файл.close()
            print("Файл закрыт")

Без finally существует риск оставить файл открытым при возникновении ошибки, что может привести к утечке ресурсов.

Работа с соединениями к базе данных

import sqlite3

def получить_пользователей(путь_к_бд):
    соединение = None
    курсор = None
    try:
        # Устанавливаем соединение с базой данных
        соединение = sqlite3.connect(путь_к_бд)
        курсор = соединение.cursor()

        # Выполняем запрос
        курсор.execute("SELECT id, name FROM users")
        пользователи = курсор.fetchall()
        return пользователи

    except sqlite3.Error as e:
        # Обрабатываем ошибки базы данных
        print(f"Ошибка базы данных: {e}")
        return []

    finally:
        # Закрываем курсор и соединение в любом случае
        if курсор is not None:
            курсор.close()
        if соединение is not None:
            соединение.close()
            print("Соединение с базой данных закрыто")

Конструкция try / finally без except

Блок finally можно использовать совместно с try без блока except. Это удобно, когда вы не хотите перехватывать исключения, но нужно гарантировать выполнение очищающего кода.

def обработать_данные(данные):
    # Устанавливаем блокировку
    блокировка = получить_блокировку()
    блокировка.acquire()

    try:
        # Обрабатываем данные — исключения не перехватываем,
        # пусть распространяются выше по стеку
        результат = выполнить_сложную_обработку(данные)
        return результат
    finally:
        # Снимаем блокировку в любом случае
        блокировка.release()
        print("Блокировка снята")

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

Взаимодействие finally с оператором return

Поведение finally при наличии оператора return — одна из тонких особенностей Python, которую важно понимать.

finally выполняется даже при return в try

def функция_с_return():
    try:
        print("В блоке try")
        return "значение из try"  # return не мешает finally выполниться
    finally:
        print("В блоке finally")  # Это будет выполнено перед возвратом

результат = функция_с_return()
# Вывод:
# В блоке try
# В блоке finally
print(результат)  # значение из try

return в finally перекрывает return из try

Если в блоке finally есть собственный оператор return, он перекрывает возвращаемое значение из блоков try или except:

def опасная_функция():
    try:
        return "результат из try"
    finally:
        # Этот return перекроет return из try!
        return "результат из finally"

print(опасная_функция())  # результат из finally

Это поведение считается антипаттерном — использование return в finally делает код трудночитаемым и может скрывать исключения. Рекомендуется избегать return в блоке finally.

finally подавляет исключения при наличии return

def скрытое_исключение():
    try:
        raise ValueError("Это исключение будет подавлено!")
    finally:
        return "finally скрыл исключение"  # Исключение потеряно

результат = скрытое_исключение()
print(результат)  # finally скрыл исключение — исключение не возникло!

Это ещё одна причина не использовать return в блоке finally.

Реальные примеры использования

Пример 1: Запись данных в файл с гарантией закрытия

def записать_отчёт(имя_файла, данные):
    файл = None
    try:
        файл = open(имя_файла, 'w', encoding='utf-8')

        # Записываем данные построчно
        for строка in данные:
            файл.write(строка + '\n')

        print(f"Отчёт успешно записан в '{имя_файла}'")
        return True

    except IOError as e:
        print(f"Ошибка записи файла: {e}")
        return False

    finally:
        # Файл будет закрыт в любом случае
        if файл is not None:
            файл.close()

# Использование
строки = ["Строка 1", "Строка 2", "Строка 3"]
записать_отчёт("отчёт.txt", строки)

Пример 2: Транзакции в базе данных

import sqlite3

def выполнить_транзакцию(путь_к_бд, запросы):
    соединение = None
    try:
        соединение = sqlite3.connect(путь_к_бд)

        # Начинаем транзакцию
        соединение.execute("BEGIN")

        for запрос, параметры in запросы:
            соединение.execute(запрос, параметры)

        # Фиксируем изменения
        соединение.commit()
        print("Транзакция успешно выполнена")
        return True

    except sqlite3.Error as e:
        # Откатываем транзакцию при ошибке
        if соединение:
            соединение.rollback()
        print(f"Ошибка транзакции, откат выполнен: {e}")
        return False

    finally:
        # Закрываем соединение в любом случае
        if соединение is not None:
            соединение.close()

Пример 3: Измерение времени выполнения

import time

def замерить_время(функция, *аргументы):
    время_начала = time.time()
    try:
        результат = функция(*аргументы)
        return результат
    except Exception as e:
        print(f"Функция завершилась с ошибкой: {e}")
        raise
    finally:
        # Время будет замерено независимо от успеха или ошибки
        время_окончания = time.time()
        elapsed = время_окончания - время_начала
        print(f"Время выполнения: {elapsed:.4f} сек.")

Сравнение с менеджерами контекста (оператор with)

В современном Python для работы с ресурсами рекомендуется использовать менеджеры контекста через оператор with. Это более чистый и идиоматичный способ гарантировать освобождение ресурсов.

try/finally vs with — сравнение на примере файла

Старый способ с try/finally:

файл = None
try:
    файл = open("данные.txt", 'r', encoding='utf-8')
    содержимое = файл.read()
    # обработка данных
finally:
    if файл is not None:
        файл.close()

Современный способ с менеджером контекста:

# with автоматически вызывает close() при выходе из блока
with open("данные.txt", 'r', encoding='utf-8') as файл:
    содержимое = файл.read()
    # обработка данных

Оба подхода гарантируют закрытие файла, но вариант с with значительно чище и короче.

Когда использовать finally, а когда with

Ситуация Рекомендуемый подход
Работа с файлами with open(...)
Соединения с БД (через современные библиотеки) Менеджер контекста библиотеки
Сетевые сокеты with socket.socket(...)
Нестандартная логика очистки finally
Объекты без поддержки with finally
Несколько шагов инициализации с разной обработкой ошибок finally

Менеджеры контекста реализуются через методы __enter__ и __exit__. Когда объект поддерживает протокол контекстного менеджера — используйте with. В остальных случаях finally остаётся надёжным инструментом.

# Создание собственного менеджера контекста
from contextlib import contextmanager

@contextmanager
def управление_ресурсом():
    ресурс = создать_ресурс()
    try:
        yield ресурс  # Передаём ресурс в блок with
    finally:
        # Этот код аналогичен блоку finally
        освободить_ресурс(ресурс)

# Использование
with управление_ресурсом() as р:
    использовать(р)

Обратите внимание: внутри @contextmanager всё равно используется try/finally — это показывает, что finally является фундаментальной концепцией.

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

  • Использование return в блоке finally — перекрывает возвращаемое значение из try/except и может скрывать исключения. Избегайте этого.
  • Использование raise в блоке finally — подавляет исходное исключение из try и заменяет его новым. Такое поведение почти никогда не является намеренным.
  • Нет проверки на None перед закрытием ресурса — если ресурс не был создан (ошибка в конструкторе), попытка закрыть None вызовет AttributeError. Всегда инициализируйте переменную ресурса значением None до блока try.
  • Слишком много логики в finally — блок finally должен содержать только код освобождения ресурсов. Бизнес-логику там размещать не стоит.
  • Игнорирование исключений в finally — если код в блоке finally сам вызовет исключение, исходное исключение из try будет потеряно. При необходимости оберните код в finally в дополнительный try/except.
# Плохо — если close() вызовет исключение, исходная ошибка потеряется
finally:
    файл.close()

# Лучше — сохраняем исходное исключение
finally:
    try:
        файл.close()
    except IOError:
        pass  # Логируем, но не перебрасываем

Частозадаваемые вопросы

Всегда ли выполняется блок finally? Практически всегда. Единственные исключения: принудительное завершение процесса через os._exit(), сигнал SIGKILL от операционной системы или бесконечный цикл в самом блоке finally. В нормальных условиях работы программы finally выполняется гарантированно.

Можно ли использовать finally без except? Да. Конструкция try/finally без except абсолютно допустима. Используйте её, когда хотите гарантировать выполнение кода очистки, но не собираетесь перехватывать исключения — пусть они распространяются к вызывающему коду.

Что произойдёт, если исключение возникнет в самом блоке finally? Если в finally возникает новое исключение, оно заменяет исходное. Исходное исключение из try будет потеряно. Поэтому в блоке finally рекомендуется использовать минимальный код и при необходимости оборачивать его в дополнительный try/except.

В каком порядке выполняются try, except, else, finally? Полный порядок: try → (если исключение: except) → (если нет исключения: else) → finally. Блок else выполняется только при отсутствии исключений в try, а finally — всегда последним.

Чем finally отличается от кода после блока try/except? Код после try/except выполняется только если исключение было перехвачено или не возникло вовсе. Если исключение не было поймано, этот код выполнен не будет. Блок finally выполняется всегда — даже при непойманных исключениях.

Когда лучше использовать with вместо finally? Используйте with, когда объект поддерживает протокол менеджера контекста (файлы, соединения с БД через современные библиотеки, блокировки threading). Это чище и безопаснее. finally остаётся незаменимым для объектов без поддержки with или при сложной логике освобождения нескольких ресурсов.

Заключение

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

Ключевые моменты для запоминания: finally выполняется всегда; избегайте return внутри finally; для объектов с поддержкой протокола контекстного менеджера предпочитайте оператор with; в блоке finally размещайте только код очистки.

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

Стрелочка влевоИнструкция по работе с PythonЦелые числа в PythonСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonПочему Python выводит значение без команды printКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыСтруктура и оформление кода PythonОсновы Django с PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonОбработка исключений с помощью try/except в PythonУдаление данных в Python с помощью removeФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьPython 1 — с чего начиналась история языкаКоманда python print - полное руководство по выводу данныхПравила именования переменных в PythonПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПозиционные и именованные аргументы в PythonОбъявление переменных и управление областью видимости в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию filter в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление зависимостями requirement в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonОсновные операции со строками в PythonТипы данных в Python — обзор и рекомендацииМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с двоичными числами PythonРабота с элементами данных PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с большими числами в PythonРабота с битами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonКак найти следующее число в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

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