Python 3.7+contextvarsпонятие
contextvars.ContextVar
контекстные переменные
Контекстная переменная: значение своё у каждой задачи asyncio и каждого потока; замена threading.local в async-коде.
Синтаксис
var = contextvars.ContextVar(name, *, default=...)
В asyncio все задачи крутятся в одном потоке, поэтому threading.local у них общий и значения перетираются; ContextVar копируется в каждую задачу при create_task() и не протекает между ними. Создают её один раз на уровне модуля, а не в функции. get() без значения и без default бросает LookupError. default разделяется всеми контекстами: изменяемый список в нём — общий.
Примеры
import asyncio, contextvars, threading
local = threading.local()
request_id = contextvars.ContextVar('request_id')
async def handle(n):
local.n = n
request_id.set(n)
await asyncio.sleep(0.01 * (3 - n))
return local.n, request_id.get()
async def main():
return await asyncio.gather(*(handle(i) for i in range(3)))
print(asyncio.run(main())) # → [(2, 0), (2, 1), (2, 2)] — local перетёрт
import contextvars
user = contextvars.ContextVar('user')
print(user.get('гость')) # → гость
try:
user.get()
except LookupError:
print('значения нет') # → значения нет
См. также
Открыть в полном глоссарииdocs.python.orgСообщить о неточности