contextvars — Kontextvariablen¶
Dieses Modul stellt APIs zur Verwaltung, Speicherung und zum Zugriff auf kontextlokalen Zustand bereit. Die Klasse ContextVar wird verwendet, um Kontextvariablen zu deklarieren und mit ihnen zu arbeiten. Die Funktion copy_context() und die Klasse Context sollten zur Verwaltung des aktuellen Kontexts in asynchronen Frameworks verwendet werden.
Kontextmanager, die einen Zustand haben, sollten Kontextvariablen anstelle von threading.local() verwenden, um zu verhindern, dass ihr Zustand unerwartet in anderen Code überläuft, wenn er in nebenläufigem Code verwendet wird.
Siehe auch PEP 567 für weitere Details.
Hinzugefügt in Version 3.7.
Kontextvariablen¶
- class contextvars.ContextVar(name[, *, default])¶
Diese Klasse wird verwendet, um eine neue Kontextvariable zu deklarieren, z.B.
var: ContextVar[int] = ContextVar('var', default=42)
Der erforderliche Parameter name wird für Introspektions- und Debugging-Zwecke verwendet.
Der optionale Keyword-only Parameter default wird von
ContextVar.get()zurückgegeben, wenn kein Wert für die Variable im aktuellen Kontext gefunden wird.Wichtig: Kontextvariablen sollten auf der obersten Modulebene erstellt werden und niemals in Closures.
Context-Objekte halten starke Referenzen auf Kontextvariablen, was verhindert, dass Kontextvariablen ordnungsgemäß vom Garbage Collector bereinigt werden.- name¶
Der Name der Variablen. Dies ist eine schreibgeschützte Eigenschaft.
Hinzugefügt in Version 3.7.1.
- get([default])¶
Gibt einen Wert für die Kontextvariable für den aktuellen Kontext zurück.
Wenn kein Wert für die Variable im aktuellen Kontext vorhanden ist, gibt die Methode
den Wert des Arguments default der Methode zurück, falls es bereitgestellt wird; oder
gibt den Standardwert für die Kontextvariable zurück, wenn sie mit einem erstellt wurde; oder
löst einen
LookupErroraus.
- set(value)¶
Aufruf zum Setzen eines neuen Wertes für die Kontextvariable im aktuellen Kontext.
Das erforderliche Argument value ist der neue Wert für die Kontextvariable.
Gibt ein
Token-Objekt zurück, das verwendet werden kann, um die Variable mit der MethodeContextVar.reset()auf ihren vorherigen Wert zurückzusetzen.
- reset(token)¶
Setzt die Kontextvariable auf den Wert zurück, den sie vor der Verwendung des
ContextVar.set()-Aufrufs hatte, der das token erstellt hat.Zum Beispiel
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-Objekte werden von der Methode
ContextVar.set()zurückgegeben. Sie können an die MethodeContextVar.reset()übergeben werden, um den Wert der Variable auf den Zustand vor dem entsprechenden set zurückzusetzen.Das Token unterstützt das Protokoll für Kontextmanager, um den Wert der entsprechenden Kontextvariable beim Verlassen eines
with-Blocks wiederherzustellen.var = ContextVar('var', default='default value') with var.set('new value'): assert var.get() == 'new value' assert var.get() == 'default value'
Hinzugefügt in Version 3.14: Unterstützung für die Verwendung als Kontextmanager hinzugefügt.
- var¶
Eine schreibgeschützte Eigenschaft. Zeigt auf das
ContextVar-Objekt, das das Token erstellt hat.
- old_value¶
Eine schreibgeschützte Eigenschaft. Setzt sich auf den Wert, den die Variable vor dem Aufruf von
ContextVar.set()hatte, der das Token erstellt hat. Sie zeigt aufToken.MISSING, wenn die Variable vor dem Aufruf nicht gesetzt war.
- MISSING¶
Ein Markierungsobjekt, das von
Token.old_valueverwendet wird.
Manuelle Kontextverwaltung¶
- contextvars.copy_context()¶
Gibt eine Kopie des aktuellen
Context-Objekts zurück.Das folgende Snippet holt eine Kopie des aktuellen Kontexts und gibt alle Variablen und ihre Werte aus, die darin gesetzt sind.
ctx: Context = copy_context() print(list(ctx.items()))
Die Funktion hat eine Komplexität von O(1), d.h. sie arbeitet gleich schnell für Kontexte mit wenigen Kontextvariablen und für Kontexte, die viele davon haben.
- class contextvars.Context¶
Eine Abbildung von
ContextVarszu ihren Werten.Context()erstellt einen leeren Kontext ohne Werte darin. Um eine Kopie des aktuellen Kontexts zu erhalten, verwenden Sie die Funktioncopy_context().Jeder Thread hat seinen eigenen effektiven Stapel von
Context-Objekten. Der aktuelle Kontext ist dasContext-Objekt oben auf dem Stapel des aktuellen Threads. AlleContext-Objekte in den Stapeln gelten als betreten.Das Betreten eines Kontexts, was durch Aufrufen seiner Methode
run()geschehen kann, macht den Kontext zum aktuellen Kontext, indem er auf den Stapel des aktuellen Threads geschoben wird.Das Verlassen des aktuellen Kontexts, was durch Rückkehr aus dem Callback geschehen kann, der an die Methode
run()übergeben wird, stellt den aktuellen Kontext auf seinen Zustand vor dem Betreten des Kontexts zurück, indem der Kontext vom oberen Ende des Kontextstapels entfernt wird.Da jeder Thread seinen eigenen Kontextstapel hat, verhalten sich
ContextVar-Objekte ähnlich wiethreading.local(), wenn Werte in verschiedenen Threads zugewiesen werden.Der Versuch, einen bereits betretenen Kontext zu betreten, einschließlich Kontexten, die in anderen Threads betreten wurden, löst einen
RuntimeErroraus.Nach dem Verlassen eines Kontexts kann er später wieder betreten werden (von jedem Thread).
Alle Änderungen an
ContextVar-Werten über die MethodeContextVar.set()werden im aktuellen Kontext aufgezeichnet. Die MethodeContextVar.get()gibt den Wert zurück, der dem aktuellen Kontext zugeordnet ist. Das Verlassen eines Kontexts macht effektiv alle Änderungen rückgängig, die an Kontextvariablen vorgenommen wurden, während der Kontext betreten war (falls erforderlich, können die Werte durch erneutes Betreten des Kontexts wiederhergestellt werden).Context implementiert die
collections.abc.Mapping-Schnittstelle.- run(callable, *args, **kwargs)¶
Betritt den Context, führt
callable(*args, **kwargs)aus, und verlässt dann den Context. Gibt den Rückgabewert von callable zurück oder gibt eine Ausnahme weiter, falls eine aufgetreten ist.Beispiel
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()¶
Gibt eine flache Kopie des Kontextobjekts zurück.
- var in context
Gibt
Truezurück, wenn der context einen Wert für var gesetzt hat; gibt andernfallsFalsezurück.
- context[var]
Gibt den Wert der
ContextVar-Variable var zurück. Wenn die Variable im Kontextobjekt nicht gesetzt ist, wird einKeyErrorausgelöst.
- get(var[, default])¶
Gibt den Wert für var zurück, wenn var den Wert im Kontextobjekt hat. Gibt andernfalls default zurück. Wenn default nicht angegeben wird, wird
Nonezurückgegeben.
- iter(context)
Gibt einen Iterator über die im Kontextobjekt gespeicherten Variablen zurück.
- len(proxy)
Gibt die Anzahl der im Kontextobjekt gesetzten Variablen zurück.
- keys()¶
Gibt eine Liste aller Variablen im Kontextobjekt zurück.
- values()¶
Gibt eine Liste aller Werte der Variablen im Kontextobjekt zurück.
- items()¶
Gibt eine Liste von 2-Tupeln zurück, die alle Variablen und ihre Werte im Kontextobjekt enthalten.
asyncio-Unterstützung¶
Kontextvariablen werden nativ in asyncio unterstützt und können ohne zusätzliche Konfiguration verwendet werden. Zum Beispiel hier ein einfacher Echo-Server, der eine Kontextvariable verwendet, um die Adresse eines entfernten Clients im Task verfügbar zu machen, der diesen Client verarbeitet.
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