contextvars
— Kontextvariabler¶
Denna modul tillhandahåller API:er för att hantera, lagra och komma åt kontextlokalt tillstånd. Klassen ContextVar
används för att deklarera och arbeta med kontextvariabler. Funktionen copy_context()
och klassen Context
bör användas för att hantera den aktuella kontexten i asynkrona ramverk.
Kontexthanterare som har tillstånd bör använda kontextvariabler istället för threading.local()
för att förhindra att deras tillstånd oväntat sprids till annan kod när de används i samtidig kod.
Se även PEP 567 för ytterligare information.
Tillagd i version 3.7.
Variabler för kontext¶
- class contextvars.ContextVar(name[, *, default])¶
Denna klass används för att deklarera en ny kontextvariabel, t.ex.:
var: ContextVar[int] = ContextVar('var', default=42)
Den obligatoriska parametern name används för introspektion och felsökning.
Den valfria parametern default, som endast innehåller nyckelord, returneras av
ContextVar.get()
när inget värde för variabeln hittas i den aktuella kontexten.Viktigt: Kontextvariabler ska skapas på den översta modulnivån och aldrig i avslutningar.
Context
-objekt innehåller starka referenser till kontextvariabler vilket förhindrar att kontextvariablerna samlas in på rätt sätt.- name¶
Namnet på variabeln. Detta är en skrivskyddad egenskap.
Tillagd i version 3.7.1.
- get([default])¶
Returnerar ett värde för kontextvariabeln för den aktuella kontexten.
Om det inte finns något värde för variabeln i det aktuella sammanhanget kommer metoden att göra det:
returnera värdet för metodens standard-argument, om ett sådant har angetts; eller
returnera standardvärdet för kontextvariabeln, om den skapades med ett sådant; eller
skapa ett
LookupError
.
- set(value)¶
Anrop för att ange ett nytt värde för kontextvariabeln i den aktuella kontexten.
Det obligatoriska argumentet value är det nya värdet för kontextvariabeln.
Returnerar ett
Token
-objekt som kan användas för att återställa variabeln till dess tidigare värde via metodenContextVar.reset()
.
- reset(token)¶
Återställer kontextvariabeln till det värde den hade innan
ContextVar.set()
som skapade token användes.Till exempel:
var = ContextVar('var') token = var.set('new value') # code that uses 'var'; var.get() returns 'new value'. var.reset(token) # After the reset call the var has no value again, so # var.get() would raise a LookupError.
- class contextvars.Token¶
Token-objekt returneras av metoden
ContextVar.set()
. De kan skickas till metodenContextVar.reset()
för att återställa variabelns värde till vad det var före motsvarande set.Token stöder context manager protocol för att återställa motsvarande kontextvariabelvärde vid utgången från
with
block:var = ContextVar('var', default='default value') with var.set('new value'): assert var.get() == 'new value' assert var.get() == 'default value'
Tillagd i version 3.14: Stöd för användning som kontexthanterare har lagts till.
- var¶
En skrivskyddad egenskap. Pekar på det
ContextVar
-objekt som skapade token.
- old_value¶
En skrivskyddad egenskap. Ställs in till det värde variabeln hade före metodanropet
ContextVar.set()
som skapade token. Den pekar påToken.MISSING
om variabeln inte var inställd före anropet.
- MISSING¶
Ett markörobjekt som används av
Token.old_value
.
Manuell kontexthantering¶
- contextvars.copy_context()¶
Returnerar en kopia av det aktuella
Context
-objektet.Följande utdrag hämtar en kopia av den aktuella kontexten och skriver ut alla variabler och deras värden som har angetts i den:
ctx: Context = copy_context() print(list(ctx.items()))
Funktionen har en O(1) komplexitet, dvs. den arbetar lika snabbt för kontexter med ett fåtal kontextvariabler som för kontexter med många.
- class contextvars.Context¶
En mappning av
ContextVars
till deras värden.Context()
skapar en tom kontext utan några värden i den. För att få en kopia av den aktuella kontexten används funktionencopy_context()
.Varje tråd har sin egen effektiva stack av
Context
-objekt. Den current context är detContext
-objekt som ligger överst i den aktuella trådens stack. AllaContext
-objekt i staplarna anses vara inmatade.När man går in i en kontext, vilket kan göras genom att anropa dess
run()
-metod, blir kontexten den aktuella kontexten genom att den läggs överst i den aktuella trådens kontextstapel.När du lämnar den aktuella kontexten, vilket kan göras genom att återvända från den återuppringning som skickades till metoden
run()
, återställs den aktuella kontexten till vad den var innan kontexten angavs genom att kontexten flyttas från toppen av kontextstapeln.Eftersom varje tråd har sin egen kontextstack, beter sig
ContextVar
-objekt på ett liknande sätt somthreading.local()
när värden tilldelas i olika trådar.Försök att ange en redan angiven kontext, inklusive kontexter som angetts i andra trådar, ger upphov till ett
RuntimeError
.När du har lämnat ett sammanhang kan du senare gå in i det igen (från vilken tråd som helst).
Alla ändringar av
ContextVar
-värden via metodenContextVar.set()
registreras i den aktuella kontexten. MetodenContextVar.get()
returnerar det värde som är associerat med den aktuella kontexten. När en kontext avslutas återställs alla ändringar som gjorts i kontextvariablerna när kontexten öppnades (vid behov kan värdena återställas genom att kontexten öppnas på nytt).Kontexten implementerar gränssnittet
collections.abc.Mapping
.- run(callable, *args, **kwargs)¶
Går in i kontexten, utför
callable(*args, **kwargs)
och går sedan ut ur kontexten. Returnerar callable:s returvärde, eller sprider ett undantag om ett sådant inträffat.Exempel:
import contextvars var = contextvars.ContextVar('var') var.set('spam') print(var.get()) # 'spam' ctx = contextvars.copy_context() def main(): # 'var' was set to 'spam' before # calling 'copy_context()' and 'ctx.run(main)', so: print(var.get()) # 'spam' print(ctx[var]) # 'spam' var.set('ham') # Now, after setting 'var' to 'ham': print(var.get()) # 'ham' print(ctx[var]) # 'ham' # Any changes that the 'main' function makes to 'var' # will be contained in 'ctx'. ctx.run(main) # The 'main()' function was run in the 'ctx' context, # so changes to 'var' are contained in it: print(ctx[var]) # 'ham' # However, outside of 'ctx', 'var' is still set to 'spam': print(var.get()) # 'spam'
- copy()¶
Returnerar en ytlig kopia av kontextobjektet.
- var in context
Returnerar
True
om kontexten har ett värde för var inställt; returnerarFalse
annars.
- context[var]
Returnerar värdet på variabeln var
ContextVar
. Om variabeln inte är inställd i context-objektet, uppstår ettKeyError
.
- get(var[, default])¶
Returnerar värdet för var om var har värdet i kontextobjektet. Returnerar annars default. Om default inte anges, returneras
None
.
- iter(context)
Returnerar en iterator över de variabler som finns lagrade i kontextobjektet.
- len(proxy)
Returnerar antalet variabler som ställts in i kontextobjektet.
- keys()¶
Returnerar en lista över alla variabler i kontextobjektet.
- values()¶
Returnerar en lista över alla variablers värden i kontextobjektet.
- items()¶
Returnerar en lista med 2-tuples som innehåller alla variabler och deras värden i kontextobjektet.
stöd för asyncio¶
Kontextvariabler stöds inbyggt i asyncio
och är redo att användas utan någon extra konfiguration. Här är till exempel en enkel ekoserver som använder en kontextvariabel för att göra adressen till en fjärrklient tillgänglig i den Task som hanterar den klienten:
import asyncio
import contextvars
client_addr_var = contextvars.ContextVar('client_addr')
def render_goodbye():
# The address of the currently handled client can be accessed
# without passing it explicitly to this function.
client_addr = client_addr_var.get()
return f'Good bye, client @ {client_addr}\r\n'.encode()
async def handle_request(reader, writer):
addr = writer.transport.get_extra_info('socket').getpeername()
client_addr_var.set(addr)
# In any code that we call is now possible to get
# client's address by calling 'client_addr_var.get()'.
while True:
line = await reader.readline()
print(line)
if not line.strip():
break
writer.write(b'HTTP/1.1 200 OK\r\n') # status line
writer.write(b'\r\n') # headers
writer.write(render_goodbye()) # body
writer.close()
async def main():
srv = await asyncio.start_server(
handle_request, '127.0.0.1', 8081)
async with srv:
await srv.serve_forever()
asyncio.run(main())
# To test it you can use telnet or curl:
# telnet 127.0.0.1 8081
# curl 127.0.0.1:8081