Ответ
Контекстный менеджер в Python — это объект, который определяет контекст выполнения для блока кода с помощью методов __enter__() и __exit__(). Он гарантирует, что определенные операции (например, выделение и освобождение ресурсов) будут выполнены до и после этого блока, даже если внутри возникло исключение. Используется с оператором with.
Зачем это нужно? Для безопасного управления ресурсами (файлы, сетевые соединения, транзакции БД, блокировки), чтобы избежать утечек и обеспечить корректное завершение.
Пример использования встроенного контекстного менеджера для работы с файлами:
# Без контекстного менеджера (потенциально небезопасно)
f = open('file.txt', 'r')
try:
data = f.read()
finally:
f.close() # Необходимо не забыть закрыть
# С контекстным менеджером (рекомендуемый способ)
with open('file.txt', 'r') as f:
data = f.read()
# Файл автоматически закрыт здесь, даже если при чтении возникла ошибка
Создание собственного контекстного менеджера:
-
Как класс:
class Timer: def __init__(self, name): self.name = name def __enter__(self): self.start = time.time() return self # Объект, который будет связан с переменной после 'as' def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.time() print(f'{self.name}: {self.end - self.start:.2f} сек') with Timer('Мой код') as t: time.sleep(1) # Вывод: Мой код: 1.00 сек -
С использованием
contextlib.contextmanager(как генератор):from contextlib import contextmanager import time @contextmanager def timer(name): start = time.time() yield # Выполнение переходит в блок 'with' end = time.time() print(f'{name}: {end - start:.2f} сек') with timer('Замер'): time.sleep(0.5)
Ответ 18+ 🔞
А, ну это же про контекстные менеджеры в питоне! Ёпта, вещь-то архиполезная, а многие её как мантру читают, не вникая. Сейчас разжуём, блядь.
Представь себе, что у тебя есть ресурс — ну, файл, сетевое соединение, какая-нибудь блокировка. И его надо обязательно закрыть, отпустить, откатить — короче, прибрать за собой. А если забудешь — будет тебе утечка памяти, сокеты висеть, файлы заблокированы. Доверия ебать ноль к самому себе, что не забудешь.
Вот раньше-то писали так, ёбаный насос:
f = open('file.txt', 'r')
try:
data = f.read()
finally:
f.close() # Необходимо не забыть закрыть
Смотри, какая хитрая жопа: открыл файл, потом try/finally на всякий пожарный, чтобы даже если ошибка — файл закрылся. Громоздко, сука, можно и забыть. А питон — он для ленивых, блядь. Поэтому придумали оператор with.
Тот же самый процесс, но в разы короче и надёжнее:
with open('file.txt', 'r') as f:
data = f.read()
# Файл автоматически закрыт здесь, даже если при чтении возникла ошибка
Вот это, блядь, красота! Всё. Ты как бы говоришь: "Заходи в контекст (with), работай с файлом f, а как выйдешь — я сам всё закрою, не парься". И не важно, вылетело ли исключение посередине или нет — файл закроется. Волнение ебать на нуле.
А если хочешь свой такой шлюз придумать? Ну, например, замер времени выполнения блока. Овердохуища просто. Есть два пути.
Первый — через класс. Надо сделать методы __enter__ и __exit__.
class Timer:
def __init__(self, name):
self.name = name
def __enter__(self):
self.start = time.time()
return self # Объект, который будет связан с переменной после 'as'
def __exit__(self, exc_type, exc_val, exc_tb):
self.end = time.time()
print(f'{self.name}: {self.end - self.start:.2f} сек')
with Timer('Мой код') as t:
time.sleep(1)
# Вывод: Мой код: 1.00 сек
Смотри, как чётко: заходишь в with — вызывается __enter__, засекается время. Выходишь — вызывается __exit__, выводится результат. Если внутри блока пиздец случится (исключение), эти параметры exc_type, exc_val, exc_tb тебе про него расскажут. Можешь их обработать или проигнорировать — да похуй.
Второй способ — через декоратор @contextmanager. Это для тех, кому терпения ноль ебать городить целый класс.
from contextlib import contextmanager
import time
@contextmanager
def timer(name):
start = time.time()
yield # Выполнение переходит в блок 'with'
end = time.time()
print(f'{name}: {end - start:.2f} сек')
with timer('Замер'):
time.sleep(0.5)
Вот это, блядь, ебушки-воробушки! Всё то же самое, но в виде функции-генератора. Всё до yield — это аналог __enter__. Сам yield — это место, где управление передаётся в твой блок кода. А всё после yield — это __exit__, который выполнится при выходе.
Короче, суть в чём: не доверяй себе, что ты всё правильно закроешь. Доверь это питону через with. И живи спокойно, я тебя в сраку чих-пых.