Erstellen Sie einen „mit“ Block auf mehr Kontext-Manager? [Duplikat]
-
26-09-2019 - |
Frage
Diese Frage bereits eine Antwort hier:
- Mehrere Variablen in a 'mit' Aussage? 6 Antworten
Angenommen, Sie drei Objekte haben Sie über das Kontextmanager erwerben, zum Beispiel ein Schloss, eine DB-Verbindung und eine IP-Buchse. Sie können sie erwerben durch:
with lock:
with db_con:
with socket:
#do stuff
Aber ist es eine Möglichkeit, es in einem Block zu tun? so etwas wie
with lock,db_con,socket:
#do stuff
Darüber hinaus ist es möglich, da eine Reihe von unbekannter Länge von Objekten, die Kontext-Manager haben, ist es möglich, irgendwie zu tun:
a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
#now all objects in array are acquired
Wenn die Antwort „nein“ ist, ist es, weil die Notwendigkeit für ein solches Feature schlechtes Design impliziert, oder vielleicht sollte ich es in einem pep vorschlagen? :-P
Lösung
In Python 2.7 und 3.1 und höher , können Sie schreiben:
with A() as X, B() as Y, C() as Z:
do_something()
Dies ist normalerweise die beste Methode zu verwenden, aber wenn Sie eine unbekannte Länge Liste der Kontext-Manager haben werden Sie eine der folgenden Methoden benötigen.
In Python 3.3 können Sie eine unbekannte Länge Liste der Kontext-Manager eingeben, indem Sie mit contextlib.ExitStack :
with ExitStack() as stack:
for mgr in ctx_managers:
stack.enter_context(mgr)
# ...
So können Sie die Kontext-Manager erstellen, wie Sie sie an die ExitStack
hinzufügen, die das mögliche Problem mit contextlib.nested
verhindert (unten erwähnt).
contextlib2 bietet eine Rückportierung von ExitStack
für Python 2.6 und 2.7.
In Python 2.6 und unter können Sie a href verwenden <= "https://docs.python.org/2/library/contextlib.html#contextlib.nested" rel = "noreferrer „> contextlib.nested
:
from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
do_something()
entspricht:
m1, m2, m3 = A(), B(), C()
with m1 as X:
with m2 as Y:
with m3 as Z:
do_something()
Beachten Sie, dass dies nicht genau das gleiche, wie sie normalerweise verschachtelten with
verwenden, weil A()
, B()
und C()
werden all zunächst aufgerufen werden, bevor die Kontextmanager eingeben. Dies wird nicht korrekt funktionieren, wenn eine dieser Funktionen eine Ausnahme auslöst.
contextlib.nested
wird in neueren Python-Versionen zugunsten der oben genannten Methoden veraltet.
Andere Tipps
Der erste Teil Ihrer Frage ist möglich in Python 3.1 .
Mit mehr als einem Elemente wird der Kontext-Manager verarbeitet, als ob mehrere mit Anweisungen verschachtelt wurden:
with A() as a, B() as b: suite
entspricht
with A() as a: with B() as b: suite
Changed in Version 3.1 : Unterstützung für mehrere Kontextausdrücke
@ interjay Antwort ist richtig. Wenn Sie jedoch müssen diese für lange Kontext-Manager tun, zum Beispiel mock.patch Kontext-Manager, dann merkt man schnell Sie diese über mehrere Zeilen brechen wollen. Stellt sich heraus, man kann sie nicht in Pars wickeln, so dass Sie Schrägstriche haben zu verwenden. Hier ist, wie das aussieht:
with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
do_something()
Der zweite Teil Ihrer Frage ist gelöst mit contextlib.ExitStack
in Python 3.3 .
auf Anschluss von @ sage88 Antwort können Sie immer diese Patches zuweisen sinnvolle Variablennamen haben, bevor Sie in sie eingegeben werden.
Sie können diese Patches in mehreren Zeilen erstellen
a_patch = mock.patch('aaaaaaa')
b_patch = mock.patch('bbbbbbb')
c_patch = mock.patch('ccccccc')
with a_patch as a, b_patch as b, as c:
do_something()