2012-09-26 13 views
10

Sto definendo una classe di gestione del contesto e vorrei poter saltare il blocco di codice senza generare un'eccezione se determinate condizioni sono soddisfatte durante l'istanziazione. Ad esempio,Esecuzione saltata di -with- block

class My_Context(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Exiting...' 
      CODE TO EXIT PREMATURELY 
    def __exit__(self, type, value, traceback): 
     print 'Exiting...' 

with My_Context(mode=1): 
    print 'Executing block of codes...' 
+0

Ho trovato questo, ma non so come dare un senso, né come implementarlo. http://www.python.org/dev/peps/pep-0377/ Ci sono altri modi più eleganti? –

+0

Il fatto che si tratti di un PEP (e della discussione sui cambiamenti semantici) suggerisce che non può essere implementato senza ricorrere al cambiamento del comportamento dell'interprete. – nneonneo

+2

ossessionato dalla pulizia? :) con A(), B(): dove B's __enter__ può sollevare qualcosa mi sembra soddisfacente. – swang

risposta

8

Se si desidera una soluzione ad hoc che utilizza le idee da withhacks (in particolare da AnonymousBlocksInPython), questo funzionerà:

import sys 
import inspect 

class My_Context(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Met block-skipping criterion ...' 
      # Do some magic 
      sys.settrace(lambda *args, **keys: None) 
      frame = inspect.currentframe(1) 
      frame.f_trace = self.trace 
    def trace(self, frame, event, arg): 
     raise 
    def __exit__(self, type, value, traceback): 
     print 'Exiting context ...' 
     return True 

Confrontare il seguente:

with My_Context(mode=1): 
    print 'Executing block of code ...' 

con

with My_Context(mode=0): 
    print 'Executing block of code ... ' 
+0

Questo è quello che stavo cercando. tytytytytyty. –

+1

Vedo, quindi innesca un TypeError in qualche modo che viene catturato e soppresso dal metodo __exit __(). Interessante lavoro intorno! –

+0

Ho aggiunto un ciclo if nel metodo __exit __() per verificare il tipo e il valore in modo che venga soppressa solo l'eccezione generata dall'hack. –

1

Quello che stai cercando di fare non è possibile, sfortunatamente. Se __enter__ solleva un'eccezione, tale eccezione viene sollevata nell'istruzione with (__exit__ non viene chiamato). Se non genera un'eccezione, il valore di ritorno viene inviato al blocco e il blocco viene eseguito.

cosa più vicina che riuscivo a pensare è una bandiera a scacchi in modo esplicito dal blocco:

class Break(Exception): 
    pass 

class MyContext(object): 
    def __init__(self,mode=0): 
     """ 
     if mode = 0, proceed as normal 
     if mode = 1, do not execute block 
     """ 
     self.mode=mode 
    def __enter__(self): 
     if self.mode==1: 
      print 'Exiting...' 
     return self.mode 
    def __exit__(self, type, value, traceback): 
     if type is None: 
      print 'Normal exit...' 
      return # no exception 
     if issubclass(type, Break): 
      return True # suppress exception 
     print 'Exception exit...' 

with MyContext(mode=1) as skip: 
    if skip: raise Break() 
    print 'Executing block of codes...' 

Questo consente anche di alzare Break() nel bel mezzo di un blocco with per simulare un normale break dichiarazione.

+0

Il flag funziona, ma vorrei mantenere tutti i controlli all'interno del gestore di contesto e mantenere pulito il blocco dei codici. Se è impossibile, potrei dover trovare un altro modo oltre a. Grazie mille! –

10

Secondo PEP-343, una dichiarazione with traduce dal:

with EXPR as VAR: 
    BLOCK 

a:

mgr = (EXPR) 
exit = type(mgr).__exit__ # Not calling it yet 
value = type(mgr).__enter__(mgr) 
exc = True 
try: 
    try: 
     VAR = value # Only if "as VAR" is present 
     BLOCK 
    except: 
     # The exceptional case is handled here 
     exc = False 
     if not exit(mgr, *sys.exc_info()): 
      raise 
     # The exception is swallowed if exit() returns true 
finally: 
    # The normal and non-local-goto cases are handled here 
    if exc: 
     exit(mgr, None, None, None) 

Come si può vedere, non c'è nulla di ovvio che si può fare dalla chiamata al metodo di __enter__() il gestore del contesto che può saltare il corpo ("BLOCK") dell'istruzione with.

Le persone hanno fatto cose specifiche dell'implementazione di Python, come la manipolazione dello stack di chiamate all'interno di __enter__(), in progetti come withhacks. Ricordo che Alex Martelli pubblicò un interessante con-hack su stackoverflow uno o due anni fa (non ricordo abbastanza post del post per cercare e trovarlo).

Ma la semplice risposta alla tua domanda/problema è che non puoi fare quello che stai chiedendo, saltando il corpo della frase con, senza ricorrere alla cosiddetta "magia profonda" (che non è necessariamente portatile tra python implementazioni). Con la magia profonda, potresti essere in grado di farlo, ma io consiglio solo di fare cose come un esercizio nel vedere come potrebbe essere fatto, mai in "codice di produzione".

+1

oh mio dio 'withhacks' è folle, hanno creato blocchi in stile Ruby in Python ... – nneonneo

+0

OK, questo spiega molto. Ho controllato withhacks. Penso che sia oltre me a questo punto ... Non so come posso usare il codice per eseguire il salto ma ci sono sicuramente frammenti di codice interessanti che potrei usare. (Aggiornamento: RUBY STYLE BLOCKS? Vedo, hahahah. È davvero assurdo) Altrimenti, avrò davvero bisogno di pensare a un altro approccio. Grazie! –