Антон Ларичев
Контекстный менеджер with в Python — как работает и зачем нужен
Введение
Когда ты работаешь с файлами, сетевыми соединениями или блокировками в Python, нужно всегда помнить о правильном освобождении ресурсов. Если забыть закрыть файл или разорвать соединение — программа может работать некорректно, утечь память или заблокировать данные. Именно для этого в Python существует оператор with и контекстные менеджеры — они автоматически берут на себя управление ресурсами.
В этой статье разберём, как устроен оператор with, зачем он нужен и в каких ситуациях его стоит использовать.
Проблема без контекстного менеджера
Представь, что ты открываешь файл для чтения:
# Открываем файл вручную
file = open("data.txt", "r")
content = file.read()
print(content)
file.close() # Не забыть закрыть!
Всё работает, пока между open() и close() не произойдёт ошибка. Если исключение вылетит до file.close(), файл останется открытым:
file = open("data.txt", "r")
content = file.read()
result = int(content) # Если в файле не число — будет ValueError
file.close() # Эта строка не выполнится при ошибке
Можно обернуть в try/finally:
file = open("data.txt", "r")
try:
content = file.read()
result = int(content)
finally:
file.close() # Закроется в любом случае
Но это громоздко. Оператор with решает эту задачу элегантнее.
Как работает оператор with
Оператор with гарантирует, что ресурс будет корректно освобождён независимо от того, произошла ошибка или нет:
# Файл закроется автоматически после выхода из блока with
with open("data.txt", "r") as file:
content = file.read()
result = int(content)
# Здесь file уже закрыт — даже если было исключение
Под капотом with вызывает два специальных метода объекта:
__enter__()— вызывается при входе в блокwith. Возвращает объект, который присваивается переменной послеas__exit__()— вызывается при выходе из блока, даже если произошло исключение
Вот что происходит шаг за шагом:
# Это:
with open("data.txt") as f:
data = f.read()
# Эквивалентно этому:
manager = open("data.txt")
f = manager.__enter__()
try:
data = f.read()
finally:
manager.__exit__(None, None, None)
Работа с файлами
Самый частый случай использования with — работа с файлами:
# Чтение файла
with open("config.json", "r", encoding="utf-8") as f:
config = f.read()
print(config)
# Запись в файл
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Привет, мир!\n")
f.write("Вторая строка\n")
# Дозапись в файл
with open("log.txt", "a", encoding="utf-8") as f:
f.write("Новая запись в лог\n")
Если вы хотите детальнее изучить Python и его возможности — приходите на наш большой курс Основы Python. На курсе 209 уроков и 34 упражнения, AI-тренажёры для практики 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.
Работа с несколькими ресурсами
Можно открывать несколько ресурсов в одном операторе with:
# Копируем содержимое одного файла в другой
with open("source.txt", "r") as src, open("dest.txt", "w") as dst:
for line in src:
dst.write(line.upper())
Начиная с Python 3.10 можно использовать скобки для переноса на несколько строк:
with (
open("source.txt", "r") as src,
open("dest.txt", "w") as dst,
open("log.txt", "a") as log,
):
for line in src:
dst.write(line.upper())
log.write(f"Обработана строка: {line.strip()}\n")
Работа с блокировками (threading)
Контекстные менеджеры незаменимы при работе с многопоточностью:
import threading
lock = threading.Lock()
# Без with — легко забыть отпустить блокировку
lock.acquire()
try:
# Критическая секция
shared_data += 1
finally:
lock.release()
# С with — блокировка освобождается автоматически
with lock:
# Критическая секция
shared_data += 1
Работа с базами данных
Многие библиотеки для баз данных поддерживают контекстные менеджеры:
import sqlite3
# Соединение закроется автоматически
with sqlite3.connect("mydb.sqlite") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)
# При успешном выходе — автоматический commit
# При исключении — автоматический rollback
Вложенные контекстные менеджеры
Контекстные менеджеры можно вкладывать друг в друга:
import json
with open("config.json", "r", encoding="utf-8") as f:
config = json.load(f)
db_path = config["database"]
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT count(*) FROM users")
count = cursor.fetchone()[0]
print(f"Всего пользователей: {count}")
Частые ошибки
- Использование переменной после выхода из блока with. После закрытия файла обращение к нему вызовет
ValueError: I/O operation on closed file:
with open("data.txt") as f:
pass
f.read() # Ошибка — файл уже закрыт
- Забыть ключевое слово as. Без
asконтекстный менеджер работает, но ты не получишь ссылку на объект:
with open("data.txt"):
pass # Файл откроется и закроется, но прочитать его не получится
- Путать with и обычное присваивание. Оператор
with— это не просто присваивание, а управление жизненным циклом ресурса. Нельзя написатьf = with open(...).
Частозадаваемые вопросы
Можно ли использовать with без as?
Да. Если тебе не нужна ссылка на объект, можно написать with some_manager(): без as. Это полезно, например, при подавлении исключений или измерении времени.
Что происходит при исключении внутри блока with?
Метод __exit__() всё равно вызывается. Он получает информацию об исключении и может его подавить, вернув True. Если __exit__() возвращает False или None — исключение продолжит распространяться.
Работает ли with с любыми объектами?
Нет, только с объектами, у которых определены методы __enter__() и __exit__(). Такие объекты называются контекстными менеджерами. Большинство стандартных ресурсов Python (файлы, сокеты, блокировки) уже поддерживают этот протокол.
Заключение
Оператор with — один из самых полезных инструментов Python для безопасной работы с ресурсами. Он гарантирует корректное освобождение ресурсов, делает код чище и защищает от утечек. Используй with везде, где работаешь с файлами, соединениями, блокировками и другими ресурсами, требующими явного закрытия.
Для закрепления навыков работы с Python рекомендуем курс Основы Python. В первых 3 модулях курса доступно бесплатное содержание, что позволяет начать изучение основ Python и понять структуру курса до покупки полного доступа.
Постройте личный план изучения Python до уровня Middle — бесплатно!
Python — часть карты развития Backend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Python
Лучшие курсы по теме

Основы Python
Антон Ларичев
Nest.js с нуля
Антон Ларичев