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
withblock: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.MISSINGom 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
ContextVarstill 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
Trueom kontexten har ett värde för var inställt; returnerarFalseannars.
- 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