Протокол __enter__ и __exit__
Создавайте менеджеры контекста с помощью специального протокола.
«Протокол __enter__ и __exit__» — бесплатный урок Python Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.
Протокол менеджера контекста
Любой объект, определяющий __enter__ и __exit__, является менеджером контекста и может использоваться с with.
class Managed:
def __enter__(self):
print("entering")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("exiting")
return False
with Managed() as m:
print("inside")Возвращаемое значение __enter__
__enter__ вызывается в начале блока with. Его возвращаемое значение присваивается переменной после as.
class Resource:
def __enter__(self):
self.value = 42
return self # bound to 'r'
def __exit__(self, *args):
self.value = None
return False
with Resource() as r:
print(r.value) # 42Аргументы __exit__
__exit__(exc_type, exc_val, exc_tb) получает сведения об исключении, если произошла ошибка. Если исключения нет, все три значения равны None.
class Safe:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is ValueError:
print(f"Suppressed ValueError: {exc_val}")
return True # suppress the exception
return False # re-raise anything else
with Safe():
raise ValueError("oops") # suppressedПодавление исключений
Верните True из __exit__, чтобы подавить исключение. Верните False или ничего не возвращайте, чтобы передать его дальше.
class Ignore:
def __enter__(self): return self
def __exit__(self, *args): return True # swallow all exceptions
with Ignore():
1 / 0
print("continues here")Пример подключения к базе данных
Классический вариант использования: получить подключение при входе, зафиксировать изменения или выполнить откат, а затем закрыть подключение при выходе.
class DBConn:
def __enter__(self):
self.conn = connect_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.conn.rollback()
else:
self.conn.commit()
self.conn.close()
return FalseМенеджер контекста Timer
Измеряйте прошедшее время: сохраните момент начала в __enter__, а в __exit__ вычислите разницу.
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *args):
self.elapsed = time.perf_counter() - self.start
print(f"Elapsed: {self.elapsed:.3f}s")
return False
with Timer():
sum(range(1_000_000))Вложенные менеджеры контекста
Несколько менеджеров контекста можно объединить в одной строке with.
with open("in.txt") as src, open("out.txt", "w") as dst:
dst.write(src.read())Повторно используемые и одноразовые менеджеры
Менеджеры контекста, у которых __enter__ каждый раз выделяет новый ресурс, можно использовать повторно. Менеджеры, исчерпывающие внутреннее состояние (например, блокировка, удерживаемая только один раз), могут быть непригодны для повторного использования.
class Reusable:
def __enter__(self):
return []
def __exit__(self, *a):
return False
for _ in range(3):
with Reusable() as lst:
lst.append(1)
print(lst) # [1] each time__aenter__ и __aexit__ (асинхронный режим)
Для асинхронных менеджеров контекста определите корутины __aenter__ и __aexit__ и используйте async with.
class AsyncDB:
async def __aenter__(self):
self.conn = await connect()
return self.conn
async def __aexit__(self, *args):
await self.conn.close()
# async with AsyncDB() as conn:
# ...Обёртка файловых объектов
Файловые объекты Python уже реализуют протокол менеджера контекста — никогда не вызывайте f.close() вручную.
with open("data.txt", "r") as f:
data = f.read()
# file is closed here automatically
print(f.closed) # TrueРеализация блокировки
Блокировки потоков являются менеджерами контекста: __enter__ захватывает блокировку, а __exit__ освобождает её.
import threading
lock = threading.Lock()
def update_shared():
with lock:
# critical section
passБыстрая проверка
Что должен возвращать __exit__, чтобы подавить исключение?
Итоги
Реализуйте __enter__ и __exit__, чтобы создавать менеджеры контекста. Возвращайте ресурс из __enter__, а в __exit__ обрабатывайте очистку и подавление исключений.
Часто задаваемые вопросы
Урок «Протокол __enter__ и __exit__» бесплатный?
Да — полный текст урока «Протокол __enter__ и __exit__» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.
Чему я научусь в уроке «Протокол __enter__ и __exit__»?
Создавайте менеджеры контекста с помощью специального протокола. Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Python Academy?
Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Протокол __enter__ и __exit__»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Python Academy?
Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Оператор with подробно
- Протокол __enter__ и __exit__
- contextlib: @contextmanager
- Практические шаблоны менеджеров контекста