contextvars.md
1> **Источник:** https://python-all.ru/3.13/library/contextvars.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `contextvars` – Контекстные переменные89---1011Этот модуль предоставляет API для управления, хранения и доступа к контекстно-локальному состоянию. Класс [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar) используется для объявления и работы с *контекстными переменными*. Функция [`copy_context()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.copy_context) и класс [`Context`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Context) должны использоваться для управления текущим контекстом в асинхронных фреймворках.1213Менеджеры контекста, имеющие состояние, должны использовать контекстные переменные вместо [`threading.local()`](https://python-all.ru/3.13/library/threading.html#threading.local), чтобы предотвратить непреднамеренную утечку своего состояния в другой код при использовании в конкурентном коде.1415См. также [**PEP 567**](https://python-all.ru/3.13/library/contextvars.html) для получения дополнительных сведений.1617Добавлено в версии 3.7.1819## Контекстные переменные2021#### `class contextvars.ContextVar(name[, *, default])`2223Этот класс используется для объявления новой контекстной переменной, например:2425```python26var: ContextVar[int] = ContextVar('var', default=42)27```2829Обязательный параметр *name* используется для целей интроспекции и отладки.3031Необязательный параметр *default*, передаваемый только по ключу, возвращается [`ContextVar.get()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.get), если в текущем контексте не найдено значение для переменной.3233**Важно:** Контекстные переменные должны создаваться на верхнем уровне модуля, а не в замыканиях. Объекты [`Context`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Context) содержат сильные ссылки на контекстные переменные, что препятствует их корректной сборке мусора.3435`ContextVar` являются [обобщёнными](https://python-all.ru/3.13/library/typing.html#generics) по типу содержащегося в них значения.3637#### `name`3839Имя переменной. Это свойство только для чтения.4041Добавлено в версии 3.7.1.4243#### `get([default])`4445Возвращает значение для контекстной переменной в текущем контексте.4647Если в текущем контексте нет значения для переменной, метод:4849- возвращает значение аргумента *default* метода, если он был передан; или50- возвращает значение по умолчанию для контекстной переменной, если она была создана с ним; или51- возбуждает [`LookupError`](https://python-all.ru/3.13/library/exceptions.html#LookupError).5253#### `set(value)`5455Вызов для установки нового значения контекстной переменной в текущем контексте.5657Обязательный аргумент *value* – это новое значение для контекстной переменной.5859Возвращает объект [`Token`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Token), который можно использовать для восстановления предыдущего значения переменной с помощью метода [`ContextVar.reset()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.reset).6061#### `reset(token)`6263Сбрасывает контекстную переменную до значения, которое она имела до вызова [`ContextVar.set()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.set), создавшего *токен*.6465Например:6667```python68var = ContextVar('var')6970token = var.set('new value')71# код, который использует 'var'; var.get() возвращает 'new value'.72var.reset(token)7374# После вызова reset переменная снова не имеет значения, поэтому75# var.get() вызовет исключение LookupError.76```7778Один и тот же *токен* нельзя использовать дважды.7980#### `class contextvars.Token`8182Объекты *Token* возвращаются методом [`ContextVar.set()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.set). Их можно передать методу [`ContextVar.reset()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.reset), чтобы вернуть значение переменной к тому, каким оно было до соответствующего *set*. Один токен не может сбросить контекстную переменную более одного раза.8384Токены являются [обобщёнными](https://python-all.ru/3.13/library/typing.html#generics) относительно того же типа, что и [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar), который их создал.8586#### `var`8788Свойство только для чтения. Указывает на объект [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar), который создал токен.8990#### `old_value`9192Свойство только для чтения. Установлено в значение, которое переменная имела до вызова метода [`ContextVar.set()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.set), создавшего токен. Указывает на [`Token.MISSING`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Token.MISSING), если переменная не была установлена до вызова.9394#### `MISSING`9596Объект-маркер, используемый [`Token.old_value`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Token.old_value).9798## Ручное управление контекстом99100#### `contextvars.copy_context()`101102Возвращает копию текущего объекта [`Context`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Context).103104Следующий фрагмент получает копию текущего контекста и выводит все переменные и их значения, установленные в нём:105106```python107ctx: Context = copy_context()108print(list(ctx.items()))109```110111Функция имеет сложность *O*(1), т.е. работает одинаково быстро для контекстов с несколькими контекстными переменными и для контекстов, содержащих их много.112113#### `class contextvars.Context`114115Отображение [`ContextVars`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar) на их значения.116117`Context()` создаёт пустой контекст без значений. Чтобы получить копию текущего контекста, используйте функцию [`copy_context()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.copy_context).118119Каждый поток имеет собственный результирующий стек объектов `Context`. [Текущий контекст](https://python-all.ru/3.13/glossary.html#term-current-context) – это объект `Context` на вершине стека текущего потока. Все объекты `Context` в стеках считаются *вошедшими*.120121*Вход* в контекст, который можно выполнить вызовом его метода [`run()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Context.run), делает контекст текущим, помещая его на вершину стека контекстов текущего потока.122123*Выход* из текущего контекста, который можно выполнить возвратом из колбэка, переданного методу [`run()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.Context.run), восстанавливает текущий контекст до состояния, в котором он был до входа, путём удаления контекста с вершины стека контекстов.124125Поскольку каждый поток имеет собственный стек контекстов, объекты [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar) ведут себя аналогично [`threading.local()`](https://python-all.ru/3.13/library/threading.html#threading.local) при присваивании значений в разных потоках.126127Попытка войти в уже вошедший контекст, включая контексты, вошедшие в других потоках, вызывает [`RuntimeError`](https://python-all.ru/3.13/library/exceptions.html#RuntimeError).128129После выхода из контекста его можно повторно войти (из любого потока).130131Любые изменения значений [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar) через метод [`ContextVar.set()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.set) записываются в текущий контекст. Метод [`ContextVar.get()`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar.get) возвращает значение, связанное с текущим контекстом. Выход из контекста эффективно отменяет любые изменения контекстных переменных, сделанные во время нахождения в контексте (при необходимости значения можно восстановить, повторно войдя в контекст).132133Контекст реализует интерфейс [`collections.abc.Mapping`](https://python-all.ru/3.13/library/collections.abc.html#collections.abc.Mapping).134135#### `run(callable, *args, **kwargs)`136137Входит в контекст, выполняет `callable(*args, **kwargs)`, затем выходит из контекста. Возвращает возвращаемое значение *вызываемого объекта* или распространяет исключение, если оно произошло.138139Пример:140141```python142import contextvars143144var = contextvars.ContextVar('var')145var.set('spam')146print(var.get()) # 'spam'147148ctx = contextvars.copy_context()149150def main():151 # 'var' было установлено в 'spam' до152 # вызова 'copy_context()' и 'ctx.run(main)', поэтому:153 print(var.get()) # 'spam'154 print(ctx[var]) # 'spam'155156 var.set('ham')157158 # Теперь, после установки 'var' в 'ham':159 print(var.get()) # 'ham'160 print(ctx[var]) # 'ham'161162# Любые изменения, которые функция 'main' вносит в 'var'163# будут содержаться в 'ctx'.164ctx.run(main)165166# Функция 'main()' была запущена в контексте 'ctx',167# поэтому изменения 'var' содержатся в нём:168print(ctx[var]) # 'ham'169170# Однако вне 'ctx' переменная 'var' по-прежнему равна 'spam':171print(var.get()) # 'spam'172```173174#### `copy()`175176Возвращает поверхностную копию объекта контекста.177178#### `var in context`179180Возвращает `True`, если в *контексте* установлено значение для *var*; в противном случае возвращает `False`.181182#### `context[var]`183184Возвращает значение переменной *var* [`ContextVar`](https://python-all.ru/3.13/library/contextvars.html#contextvars.ContextVar). Если переменная не установлена в объекте контекста, возбуждается [`KeyError`](https://python-all.ru/3.13/library/exceptions.html#KeyError).185186#### `get(var[, default])`187188Возвращает значение *var*, если *var* имеет значение в объекте контекста. В противном случае возвращает *default*. Если *default* не указан, возвращает `None`.189190#### `iter(context)`191192Возвращает итератор по переменным, хранящимся в объекте контекста.193194#### `len(proxy)`195196Возвращает количество переменных, установленных в объекте контекста.197198#### `keys()`199200Возвращает список всех переменных в объекте контекста.201202#### `values()`203204Возвращает список всех значений переменных в объекте контекста.205206#### `items()`207208Возвращает список кортежей из двух элементов, содержащих все переменные и их значения в объекте контекста.209210## Поддержка asyncio211212Контекстные переменные нативно поддерживаются в [`asyncio`](https://python-all.ru/3.13/library/asyncio.html#module-asyncio) и готовы к использованию без дополнительной настройки. Например, вот простой эхо-сервер, который использует контекстную переменную, чтобы сделать адрес удаленного клиента доступным в задаче, обрабатывающей этого клиента:213214```python215import asyncio216import contextvars217218client_addr_var = contextvars.ContextVar('client_addr')219220def render_goodbye():221 # Адрес текущего обрабатываемого клиента можно получить222 # не передавая его явно в эту функцию.223224 client_addr = client_addr_var.get()225 return f'Good bye, client @ {client_addr}\r\n'.encode()226227async def handle_request(reader, writer):228 addr = writer.transport.get_extra_info('socket').getpeername()229 client_addr_var.set(addr)230231 # В любом вызываемом нами коде теперь можно получить232 # адрес клиента, вызвав 'client_addr_var.get()'.233234 while True:235 line = await reader.readline()236 print(line)237 if not line.strip():238 break239240 writer.write(b'HTTP/1.1 200 OK\r\n') # строка состояния241 writer.write(b'\r\n') # заголовки242 writer.write(render_goodbye()) # тело243 writer.close()244245async def main():246 srv = await asyncio.start_server(247 handle_request, '127.0.0.1', 8081)248249 async with srv:250 await srv.serve_forever()251252asyncio.run(main())253254# Для тестирования можно использовать telnet или curl:255# telnet 127.0.0.1 8081256# curl 127.0.0.1:8081257```258