Веб-версияОткрыть в Telegram

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

18 мая 2026
P
Python Заметки
Ссылка
нажмите — покажем
Мы рассмотрели два способа управления конеткстом переменных. Если вам показалось, что это выглядит излишне и можно было бы оставить один, то вам не показалось. Способ с threading.local придуман для разделения переменных между потоками. CоntextVar был добавлен как новый метод для асинхронного кода, но оказался настолько универсальным, что его можно использовать и с потоками. После появления ContextVar в PEP567 его рекомендовано использовать вместо threading.local. И даже был сделан бекпорт для версий ниже 3.7.1. Теперь, если совместить ContextVar и Proxy-класс из прошлого примера то получим такой класс↗️. Но у этого класса есть две проблемы: 1️⃣ Нигде не вызывается reset для сброса переменной, что может приводить проблемам - утечка памяти - "грязный" конеткст при переиспользовании потоков - невозможность вернуться к дефолту Решим это с помощью конектстного менеджера: @contextlib.contextmanager def configure_context(self, *args, **kwargs): """Синхронный контекстный менеджер (для `with`)""" tok_cfg = self._cv_config.set((args, kwargs)) tok_obj = self._cv_object.set(None) try: yield self finally: self._cv_object.reset(tok_obj) self._cv_config.reset(tok_cfg) @contextlib.asynccontextmanager async def aconfigure_context(self, *args, **kwargs): """Асинхронный контекстный менеджер (для `async with`)""" tok_cfg = self._cv_config.set((args, kwargs)) tok_obj = self._cv_object.set(None) try: yield self finally: self._cv_object.reset(tok_obj) self._cv_config.reset(tok_cfg) Пример использования: with proxy.configure_context(val1, val2): proxy.do_something() Теперь прокси готов, но... 2️⃣ В асинхронном коде, для которого и придуманы ContextVar, созданием корутин занимается Event Loop, именно он отвечает за наследование контекста дочерними корутинами. В случае с потоками ничего такого нет, мы сами себе "эвентлуп", поэтому приходится прописывать копирование конеткста самстоятельно. Пример проблемы с отсутствием наследованием конеткста в потоках↗️ Для решения есть функция копирования текущего контекста и метод запуска функции с новым конектстом: сontextvars.copy_context().run(func, *args, **kwargs) Здесь сложно придумать универсальное автоматическое копирование контекста, самая простая функция будет выглядеть так: def run_in_thread_with_context( func: Callable, *args, **kwargs ) -> threading.Thread: ctx = contextvars.copy_context() t = threading.Thread( target=lambda: ctx.run(func, *args, **kwargs) ) t.start() return t И если вернуться к нашему синхронному ApiClient, то придётся следить за конектстом самостоятельно. И если где-то в коде библиотеки уже есть вызов тредов, то это работать не будет, придется переписывать. threading.local тоже не наследует конеткст. Полный пример Proxy с CоntextVar↗️ Пример использования: client = ContextVarProxy(ApiClient) def worker_in_thread(token): with client.configure_context(token=token): use_client(...) Еще вариант, это кастомные ThreadExecutor и Thread с поддержкой автокопирования контекста. Забираем здесь↗️ И нет, это не пример как надо делать в проде) Это просто эксперемент для понимания процесса. #tricks
5 · 664 ·

Рядом в ленте

PPython ЗаметкиВ асинхронных приложениях есть один не всегда очевидный момент, который приводит к неявным багам - это общие глобальные объекты. Да, это в целом антипаттерн, ноPPython ЗаметкиТеперь аналогичная история с тредами. Для тредов используется объект threading.local. Он позволяет создать локальный динамический атрибут (да, вот так костыльно
это сообщение
PPython ЗаметкиКак-то давно писал трансфер файлов по сети. В этом проекте требовалось создавать файл, который сразу существует на диске, имеет нужный размер но еще не содержитPPython ЗаметкиВ Python 3.6 был полностью переработан стандартный dict. Вместо разреженной таблицы данные стали храниться в плотных массивах. Это дало буст к скорости и эконом

Открытая публичная лента из поискового индекса ChatCrawler — «Google по публичному Telegram»; обновляется по мере обхода площадки. Время — UTC.

Только публичный контент, официальный API Telegram. О проекте · Вопросы · Чего мы не делаем · Убрать страницу из выдачи · Каталог · Поиск · Как мы считаем