惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
Malwarebytes
Malwarebytes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
F
Future of Privacy Forum
C
Cisco Blogs
T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
S
Securelist
K
Kaspersky official blog
S
Schneier on Security
T
ThreatConnect
T
Tenable Blog
Spread Privacy
Spread Privacy
T
True Tiger Recordings
AWS News Blog
AWS News Blog
F
Fox-IT International blog
量子位
T
Threatpost
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
宝玉的分享
宝玉的分享
腾讯CDC
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
U
Unit 42
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
小众软件
小众软件
A
About on SuperTechFans
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
博客园 - 三生石上(FineUI控件)
美团技术团队
Google Online Security Blog
Google Online Security Blog
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog

Все публикации подряд на Хабре

Глубокая интеграция месседжинга с бизнес процессами в фреймворке NodaLogic Пароль против уборщицы Выяснились детали мега-IPO SpaceX, а также первый прибыльный квартал Anthropic Люди с психическими расстройствами – новая нефть? Когда нейросети перестанут галлюцинировать? И почему на «что за дичь» они несут ещё большую дичь? Мессенджер HalChat теперь в Google Play: 3 года разработки, ИИ в браузере и квест с модерацией Реверс-инжиниринг Xiaomi Smart Band 10 Когда памяти мало Среда повседневности как объект проектирования: что общего у горца, серотониновой ямы и митохондрий AGENTS.md создавали, чтобы помогать агентам. Я использую его, чтобы их вычислять Почему устанавливают join_collapse_limit = 20 Почему устанавливают join_collapse_limit = 20 Эрик Рис, автор Lean Startup: Почему хорошие компании становятся плохими после IPO Context-driven Reusable Form Pattern: Масштабируемая архитектура для Create / Edit / Create-from-Source Пузырьковая сетка, кошачья стая и не только — неожиданные источники вдохновения для QoS-алгоритмов ___, или «Заголовок намеренно оставлен пустым» ИИ-боты сканируют даже логи TLS-сертификатов. Любая информация используется для обучения LLM Нейросеть оживить фото ИИ: Как оживить фото нейросетью в 2026 году? Разбираемся в ML без воды: от базы до Attention. Часть 5: Метрики качества В поисках «кофейного Грааля». Как человечество пытается сварить идеальный кофе и какие рецепты предлагают…математики Программатик: Часть 2 — OpenRTB Интернет до бесконечных лент: каким был 2010 год Перезапуск TrueIndex: что изменилось в рейтинге языков программирования Проектный холст: как менеджеру подбирать «краски» управления под разные команды «Метафизика в формулах: математическое ядро «Веры Паломника — Исход» Java и постквантовый TLS Marcli: Markdown Терминал Кнопочный смартфон с 5G за 2800 рублей — разбираем и изучаем китайскую диковинку Где неприятности — там и жизнь Разворачивайте платформы: stackfile Мой путь в Microsoft Мобильная разработка за неделю #631 (18 — 24 мая) Что не так с Mixtape, и почему не все довольны новой игрой? Стоматология каменного века. Как неандертальцы лечили зубы 59 тысяч лет назад Почему классическое управление проектами часто не работает в IT-продуктах Строительство Саркофага. Часть 2. Бетонные реки и стальные берега РАЗРАБОТКА ПАРАМЕТРИЗИРУЕМОГО МОДУЛЯ CORDIC-АЛГОРИТМА НА SYSTEM VERILOG Вариационное исчисление как метафора свободы выбора: от градиентного спуска к онтологии пути Ekahau Sidekick и RSSI‑offset: физические ограничения метода и пять независимых причин неточности клиентской модели Колесо потока против раскола Обзор интересных особенностей переворачивающихся при умножении чисел В С неопределённое поведение повсюду MCP-агрегатор: объединяем инструменты для LLM в один сервер Дата-центры в космосе: как Google и SpaceX готовят новую инфраструктуру для ИИ Google готовит замену Chromebook: какими будут ноутбуки Googlebook Пользователь пишет issue, агент меняет сайт. Да, я это сделал Корпоративные конфликты в ИТ-секторе: механика судебной защиты активов и субсидиарных рисков Цена одной опечатки: Как три неверные буквы сорвали киберограбление на миллиард долларов Как я победил спам в своих email аккаунтах Whitepaper Сбера «AI-Disrupt PDLC»: разбор для тех, кто пишет код RustDesk Pro в России не купить. После долгих лет администрирования мы собрали своё честное решение Не пики, а бассейны: почему эволюция — это блуждание по графу жизни Как Gemini 3.5 Flash сломали ради красивых графиков (и почему она обходит 3.1 Pro только на бумаге) Вредоносная атака на Laravel-Lang meta-attention is all you need Как перестать путаться в IP-адресах серверов Сколько стоят ошибки в арбитраже: декомпозиция ценообразования на судебные услуги в Москве Разбираемся в ML без воды: от базы до Attention. Часть 4: kNN Vortex: фреймворк для тех, кого задолбала итальянская кухня в репозитории Использование тепла ЦОД в мире и РФ Часть 4. Скорость света — технические детали Не цитируй мне нейросеть Что сейчас с Project Loom? Примеры и код Рождённые в Сумерках Meta 1 мая показала как они хранят ключи от ваших бэкапов WhatsApp. Разбираю архитектуру и сравниваю Линт проектов: собираем ESLint, Prettier и Stylelint в один пакет Reasoning-модели сломали мой промпт-инжиниринг. Год переучиваюсь РБМК: enfant terrible Как я собеседую менеджеров AI-продуктов для крупного Enterprise Парадокс рынка труда: конкуренция выросла, но не везде, нанимать легче, но не везде Модификаторы в Blender: осваиваем Boolean «Бесплатно» — это красный флаг: почему мы доверяем не тем (опрос) Стратегия выживания в эпоху ИИ Новая теория обещает переписать фундамент всей математики MTP у Qwen3.6 в llama.cpp обещает ×2 по скорости. Я прогнал ту же модель через своего агента — и получил обратное [Перевод] Соль и перец в безопасности паролей Что такое «статьи-зомби» CodeGraph: граф кода для Claude Code вместо grep по файлам. Разбираю архитектуру и проверяю бенчмарки Мессенджер Ласточка. Часть 3 Google представила Gemini Omni — универсальную ИИ-модель. Роботы работают, счастлив человек Что у SpaceX с патентным портфелем перед IPO? Делегирование, которому можно научиться у промпт‑инженеров Feature Based Clean Architecture. Часть 5: Масштабирование FBCA и теоретико-графовый анализ зависимостей Настройка типизации формы React Hook Form (≥ v7.44.0) + Zod с разными входными и выходными типами Feature Based Clean Architecture. Часть 4: FBCA: формализация границ ответственности в NestJS-модуле Корпорация «Святые Технологии». Работа мечты (рассказ) CyLab Security Academy: как Carnegie Mellon превратила CTF в полноценную обучающую платформу Feature Based Clean Architecture. Часть 3: Архитектурный риск циклов в NestJS: ROI решений на горизонте пяти лет Домашний сервер без белого IP: безопасная публикация сервисов через VPS, обратный SSH-туннель и Caddy Почему не взлетели дирижабли? Часть 22: Митягина, Эйхенвальд и Ховрина, первый в истории женский экипаж дирижабля Китайцы ответили на H200 — обзор Zhenwu M890 от Alibaba Feature Based Clean Architecture. Часть 2: Декомпозиция на сервисы: анализ ограниченности подхода Лучшие игры для Steam Deck в 2026 году по мнению пользователей Обход блокировок внутри iOS-приложения: VLESS + Reality через sing-box, и грабли по дороге [Перевод] Любой пользователь интернета может позвонить в вашу дверь Новый экспериментальный препарат для похудения обеспечил резкое снижение веса Хром и скорость Провалила вайтборд, но прошла тестовое — как я делала задание для Т-Банка Космическая линза помогла Уэббу увидеть древнейшую галактику Вселенной Почему custom URI schemes в Telegram Mini Apps ведут себя по-разному на Android, iOS и Desktop
Контекстные менеджеры в Python за пределами with open(): пишем свои и упрощаем код
badcasedaily · 2026-05-25 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение6 мин

Охват и читатели8

Туториал

Каждый разработчик знает with open("file.txt") as f. Файл открывается, читается, закрывается автоматически, даже если внутри блока произошла ошибка. Удобно, понятно, да и безопасно. Но почему-то with почти всегда встречается только рядом с файлами. А ведь контекстные менеджеры решают гораздо более широкий класс задач: управление соединениями с базой, транзакции, замер времени, временное изменение конфигурации, подавление ошибок, захват и освобождение ресурсов.

Написать свой контекстный менеджер — дело на 5-10 строк, и код после этого становится заметно чище.

Что на самом деле делает with

with вызывает два метода: enter при входе в блок и exit при выходе (в том числе при исключении). Всё, что нужно от контекстного менеджера, — реализовать эти два метода:

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.3f}s")
        return False  # не подавляем исключения

with Timer() as t:
    result = expensive_computation()

print(f"Computation took {t.elapsed:.3f}s")

enter возвращает объект, который попадает в переменную после as. exit получает информацию об исключении (если оно было) и решает, подавлять его или нет (return True подавляет, return False пробрасывает дальше).

Но писать класс ради двух методов — многословно. В стандартной библиотеке есть contextlib.contextmanager, который позволяет написать то же самое как генератор:

from contextlib import contextmanager
import time

@contextmanager
def timer(label: str = "Block"):
    start = time.perf_counter()
    try:
        yield  # здесь выполняется тело with-блока
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.3f}s")

with timer("DB query"):
    result = db.execute("SELECT * FROM orders")

Код до yield — это enter. Код после yieldfinally) — это exit. Если нужно вернуть значение через as, делаете yield value. Проще, короче, и для большинства случаев достаточно.

Управление соединениями с базой данных

Типичный паттерн: получить соединение из пула, выполнить работу, вернуть соединение в пул.

Если забыть вернуть — соединение утекает, пул исчерпается, сервис встанет.

@contextmanager
def get_connection(pool):
    conn = pool.getconn()
    try:
        yield conn
    finally:
        pool.putconn(conn)

# Использование
with get_connection(db_pool) as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE active = true")
    users = cursor.fetchall()
# Соединение вернулось в пул, даже если был exception

Без контекстного менеджера код выглядит так:

conn = pool.getconn()
try:
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE active = true")
    users = cursor.fetchall()
finally:
    pool.putconn(conn)

Формально разница невелика. Но когда у вас 50 мест в коде, где нужно соединение, try/finally на каждом — это шум, который мешает читать бизнес-логику. С контекстным менеджером управление ресурсом спрятано в одном месте, и в каждом месте использования — только with get_connection(pool) as conn.

Транзакции

Соединение + транзакция — частая комбинация. Начинаем транзакцию, если всё хорошо — commit, если exceptionrollback:

@contextmanager
def transaction(pool):
    conn = pool.getconn()
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise  # пробрасываем exception дальше
    finally:
        pool.putconn(conn)

with transaction(db_pool) as conn:
    conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
    conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
# Если второй UPDATE упал — rollback обоих

Без контекстного менеджера это try/except/finally на 10 строк в каждом месте, где нужна транзакция. С ним — одна строка with transaction(pool) as conn, и дальше только бизнес-логика.

Обратите внимание на raise в блоке except. Без него исключение будет подавлено, и вызывающий код не узнает, что транзакция откатилась. Контекстный менеджер делает cleanup (rollback), но решение о том, что делать с ошибкой, оставляет вызывающему коду.

Замер времени с контекстом

Timer из начала статьи полезен, но базовый. Версия, которая пригодится:

@contextmanager
def measure(operation: str, logger=None, warn_threshold: float = None):
    start = time.perf_counter()
    try:
        yield
    finally:
        elapsed = time.perf_counter() - start
        msg = f"{operation}: {elapsed:.3f}s"
        
        if logger:
            if warn_threshold and elapsed > warn_threshold:
                logger.warning(f"SLOW {msg}")
            else:
                logger.info(msg)
        else:
            print(msg)

with measure("fetch_users", logger=log, warn_threshold=1.0):
    users = db.fetch_all_users()

with measure("process_payment", logger=log, warn_threshold=0.5):
    process_payment(order)

Если операция заняла дольше warn_threshold, в лог пойдёт WARNING вместо INFO. В продакшене можно настроить алерт на WARNING-логи и узнавать о деградации производительности до того, как пользователи начнут жаловаться.

Этот менеджер можно комбинировать с structured logging (structlog / slog): вместо logger.info(msg) передавать поля operation и duration_ms отдельно, и фильтровать в Kibana.

Временное изменение состояния

Иногда нужно временно изменить что-то (рабочую директорию, переменные окружения, уровень логирования, настройки) и гарантированно вернуть как было:

import os

@contextmanager
def working_directory(path: str):
    original = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(original)

with working_directory("/tmp/build"):
    subprocess.run(["make", "all"])
# Вернулись в оригинальную директорию

@contextmanager
def env_var(key: str, value: str):
    original = os.environ.get(key)
    os.environ[key] = value
    try:
        yield
    finally:
        if original is None:
            del os.environ[key]
        else:
            os.environ[key] = original

with env_var("DATABASE_URL", "postgres://test:test@localhost/testdb"):
    run_tests()
# DATABASE_URL вернулся к прежнему значению (или удалён, если его не было)

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

Для тестов это особенно ценно. Тест меняет переменную окружения, запускает код, переменная восстанавливается. Следующий тест не зависит от предыдущего.

Подавление исключений

В стандартной библиотеке есть contextlib.suppress:

from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove("temp_file.txt")
# Если файла нет — ничего не произойдёт, исключение подавлено

Но можно написать свой менеджер, который не просто подавляет, а логирует:

@contextmanager
def ignore_errors(*exceptions, logger=None):
    try:
        yield
    except exceptions as e:
        if logger:
            logger.warning(f"Suppressed {type(e).__name__}: {e}")

with ignore_errors(ConnectionError, TimeoutError, logger=log):
    send_analytics_event(data)
# Если аналитика недоступна — ничего страшного, основная логика продолжит работу

Полезно для некритичных операций: отправка аналитики, обновление кеша, логирование во внешний сервис. Если они упали, основная логика не должна страдать, но знать об ошибке нужно.

Async-контекстные менеджеры

Для async-кода всё то же самое, но с async with, aenter/__aexit__ и asynccontextmanager:

from contextlib import asynccontextmanager

@asynccontextmanager
async def async_transaction(pool):
    conn = await pool.acquire()
    tr = conn.transaction()
    await tr.start()
    try:
        yield conn
        await tr.commit()
    except Exception:
        await tr.rollback()
        raise
    finally:
        await pool.release(conn)

async with async_transaction(db_pool) as conn:
    await conn.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
    await conn.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")

Принцип идентичен синхронной версии. asynccontextmanager вместо contextmanager, async def вместо def, await где нужно.

Когда не стоит писать контекстный менеджер

Если cleanup-логика нужна в одном месте, try/finally проще и читаемее: не нужно уходить в отдельную функцию, чтобы понять, что происходит. Контекстный менеджер окупается, когда один и тот же паттерн (получить ресурс, использовать, вернуть) повторяется в трёх и более местах.

Если cleanup зависит от результата работы внутри блока (не просто «вернуть соединение», а «если результат X — сделать Y, если Z — сделать W»), контекстный менеджер становится неудобным. exit получает информацию об исключении, но не о результате нормальной работы. В таких случаях проще обычный try/except/finally с явной логикой.

Если у вас есть свой опыт, делитесь в комментариях. Спасибо, что дочитали.

Кстати, если хотите глубже разобраться в теме backend-инфраструктуры, async-кода и работы с данными, обратите внимание на бесплатные открытые уроки OTUS:

Занятия проходят в рамках онлайн-курсов OTUS и позволяют познакомиться с преподавателями-практиками и форматом обучения.

Больше анонсов открытых уроков, подборок по IT-направлениям и полезных материалов публикуем на канале OTUS в MAX — подписывайтесь.