Creare un “con” blocco di parecchi gestori di contesto? [duplicare]
-
26-09-2019 - |
Domanda
Questa domanda ha già una risposta qui:
- più variabili in un 'con' affermazione? 6 risposte
Si supponga di avere tre oggetti si acquisiscono via contesto manager, per la serratura esempio A, una connessione db e una presa ip. Si possono acquistare da:
with lock:
with db_con:
with socket:
#do stuff
Ma c'è un modo per farlo in un blocco? qualcosa come
with lock,db_con,socket:
#do stuff
Inoltre, è possibile, dato un array di sconosciuta lunghezza di oggetti con gestori di contesto, è possibile fare in qualche modo:
a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
#now all objects in array are acquired
Se la risposta è "no", è perché la necessità di tale caratteristica un implica cattiva progettazione, o forse dovrei suggerire in un pep? :-P
Soluzione
Nel Python 2.7 e 3.1 e superiore , è possibile scrivere:
with A() as X, B() as Y, C() as Z:
do_something()
Questo è normalmente il metodo migliore per l'uso, ma se si dispone di un elenco sconosciuta di lunghezza del contesto manager avrete bisogno di uno dei metodi di seguito.
Nel Python 3.3 , è possibile inserire un elenco sconosciuta di lunghezza del contesto manager utilizzando contextlib.ExitStack :
with ExitStack() as stack:
for mgr in ctx_managers:
stack.enter_context(mgr)
# ...
Questo consente di creare i gestori di contesto come li sta aggiungendo al ExitStack
, che impedisce l'eventuale problema con contextlib.nested
(di cui sotto).
contextlib2 fornisce un backport di ExitStack
per Python 2.6 e 2.7.
Nel Python 2.6 e al di sotto noreferrer, è possibile utilizzare contextlib.nested
:
from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
do_something()
è equivalente a:
m1, m2, m3 = A(), B(), C()
with m1 as X:
with m2 as Y:
with m3 as Z:
do_something()
Si noti che questo non è esattamente lo stesso come normalmente utilizzando with
annidata, perché A()
, B()
e C()
saranno tutti chiamati inizialmente, prima di entrare i gestori di contesto. Questo non funzionerà correttamente se una di queste funzioni solleva un'eccezione.
contextlib.nested
è deprecato in versioni più recenti di Python a favore dei metodi di cui sopra.
Altri suggerimenti
La prima parte della tua domanda è possibile in Python 3.1 .
Con più di un oggetto, i gestori di contesto sono trattati come se multipla con le dichiarazioni sono stati nidificati:
with A() as a, B() as b: suite
è equivalente a
with A() as a: with B() as b: suite
Modificato nella versione 3.1 : Supporto per più espressioni di contesto
@ di interjay risposta è corretta. Tuttavia, se avete bisogno di fare questo per i gestori di contesto lunghi, per il contesto manager ad esempio mock.patch, poi ti rendi conto rapidamente che si desidera rompere questo attraverso le linee. Si scopre che non si possono avvolgere in parentesi, quindi bisogna utilizzare barre rovesciate. Ecco cosa che appare come:
with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
do_something()
La seconda parte della tua domanda è risolto con contextlib.ExitStack
in Python 3.3 .
A seguito di risposta di @ sage88 si può sempre assegnare tali patch ad avere i nomi delle variabili significative prima di entrare in loro.
Si potrebbe creare quelle patch in più righe
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()