Ответ
Да, постоянно использую контекстные менеджеры в Python. Это один из ключевых паттернов языка для управления ресурсами (файлы, сетевые соединения, транзакции БД), гарантирующий их корректное освобождение, даже если в блоке кода произошло исключение.
Стандартное использование с файлами и сетевыми ресурсами:
# Автоматическое закрытие файла
with open('data.json', 'r') as f:
data = json.load(f)
# Файл закрыт здесь, даже если внутри блока было исключение
# Работа с сессиями requests
with requests.Session() as session:
session.auth = ('user', 'pass')
response = session.get('https://api.example.com/data')
# Сессия корректно закрыта
Создание собственного контекстного менеджера с помощью класса:
import sqlite3
from contextlib import contextmanager
class DatabaseConnection:
def __init__(self, db_path):
self.db_path = db_path
self.conn = None
def __enter__(self):
self.conn = sqlite3.connect(self.db_path)
self.conn.execute('PRAGMA foreign_keys = ON') # Пример настройки
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.conn.commit() # Коммит, если не было ошибок
else:
self.conn.rollback() # Откат при ошибке
self.conn.close()
# Использование
with DatabaseConnection('app.db') as conn:
cursor = conn.cursor()
cursor.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
# Соединение гарантированно закрыто, транзакция завершена
Использование декоратора @contextmanager из contextlib:
from contextlib import contextmanager
import time
@contextmanager
def timer(operation_name):
"""Контекстный менеджер для замера времени выполнения."""
start = time.perf_counter()
try:
yield
finally:
end = time.perf_counter()
print(f"{operation_name} took {end - start:.4f} seconds")
with timer("Data processing"):
# Долгая операция
process_large_dataset()
В реальных проектах я применял контекстные менеджеры для управления пулами соединений с БД, временной смены текущей директории (contextlib.chdir), а также для реализации механизма блокировок (lock) при работе с многопоточным кодом.
Ответ 18+ 🔞
Ну, ты представляешь, в Питоне эта штука — контекстные менеджеры — это просто спасение, ёпта. Как будто тебе дали личного раба, который за тобой всё приберёт, даже если ты посередине операции словишь исключение и начнёшь орать как резаный. Без них — пиздец, постоянно следи, закрыл ли ты файл, откатил ли транзакцию... А с ними — красота, with написал и забыл.
Вот как это обычно выглядит, когда работаешь с файлами или сетью:
# Файл сам закроется, даже если тебя тут током шарахнет
with open('data.json', 'r') as f:
data = json.load(f)
# Всё, уже закрыт. Никаких утечек, всё чисто.
# Или вот сессия requests — тоже самое
with requests.Session() as session:
session.auth = ('user', 'pass')
response = session.get('https://api.example.com/data')
# Сессия прибита, ресурсы освобождены. Удобно, блядь.
А если надо свой, кастомный, накрутить — пожалуйста, делаем класс:
import sqlite3
from contextlib import contextmanager
class DatabaseConnection:
def __init__(self, db_path):
self.db_path = db_path
self.conn = None
def __enter__(self):
# Вот тут мы входим в контекст, создаём соединение
self.conn = sqlite3.connect(self.db_path)
self.conn.execute('PRAGMA foreign_keys = ON') # Какая-нибудь настройка
return self.conn # И отдаём его наружу
def __exit__(self, exc_type, exc_val, exc_tb):
# А тут выходим. Это святое — что бы ни случилось, соединение закроется.
if exc_type is None:
self.conn.commit() # Если всё ок — коммитим
else:
self.conn.rollback() # Если была ошибка — откатываем, ёбана
self.conn.close() # И в любом случае — нахуй закрываем
# Используем
with DatabaseConnection('app.db') as conn:
cursor = conn.cursor()
cursor.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
# Всё, блядь. Соединение убито, транзакция завершена. Спи спокойно.
Но есть способ проще, для ленивых — декоратор @contextmanager:
from contextlib import contextmanager
import time
@contextmanager
def timer(operation_name):
"""Засекаем время выполнения. Проще пареной репы."""
start = time.perf_counter()
try:
yield # Вот тут выполняется твой код внутри `with`
finally:
end = time.perf_counter()
print(f"{operation_name} заняло {end - start:.4f} секунд")
with timer("Обработка данных"):
# Какая-нибудь тяжёлая хуйня
process_large_dataset()
# И тебе на блюдечке выдали время. Красота, ядрёна вошь.
В реальных проектах я этой хуйней пользовался на овердохуища. Например, для пулов соединений к базе — чтобы не пиздеть лишние подключения. Или для временной смены рабочей директории — сделал with chdir('/tmp'):, поработал и вернулся обратно, даже думать не надо. Ну и для всяких блокировок в многопоточке — зашёл в контекст, взял лок, поработал с общими данными, вышел — лок отпустился сам. Вообще, вещь, блядь, незаменимая.