2012-09-26 7 views
10

Estoy definiendo una clase de administrador de contexto y me gustaría poder omitir el bloque de código sin generar una excepción si se cumplen ciertas condiciones durante la creación de instancias. Por ejemplo,Omitir la ejecución de -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

Encontré esto, pero no sé cómo darle sentido ni cómo implementarlo. http://www.python.org/dev/peps/pep-0377/ ¿Hay otras formas más elegantes? –

+0

El hecho de que sea un PEP (y la discusión de los cambios semánticos) sugiere que no se puede implementar sin recurrir a cambiar el comportamiento del intérprete. – nneonneo

+2

obsesionado con la pulcritud? :) con A(), B(): donde B's __enter__ puede levantar algo, me parece bien. – swang

Respuesta

8

Si desea una solución ad-hoc que utiliza las ideas de withhacks (específicamente de AnonymousBlocksInPython), esto va a funcionar:

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 

Compare los siguientes:

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

con

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

Esto es lo que estaba buscando. tytytytytyty. –

+1

Veo, por lo que desencadena un TypeError de alguna manera que es capturado y suprimido por el método __exit __(). Interesante trabajo alrededor! –

+0

Agregué un bucle if en el método __exit __() para verificar el tipo y el valor de manera que solo se suprima la excepción planteada por el hack. –

1

Lo que intenta hacer no es posible, desafortunadamente. Si __enter__ genera una excepción, esa excepción se plantea en la declaración with (no se llama al __exit__). Si no genera una excepción, el valor de retorno se alimenta al bloque y el bloque se ejecuta.

es lo más parecido que podía pensar es un indicador comprobado de manera explícita por el bloque:

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...' 

Esto también le permite elevar Break() en medio de un bloque with para simular un break declaración normal.

+0

El indicador funciona, pero me gustaría mantener todos los controles dentro del administrador de contexto y mantener limpio el bloque de códigos. Si es imposible, puede que tenga que encontrar otra manera además de con. ¡Muchísimas gracias! –

10

Según PEP-343, una declaración with traduce a partir de:

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) 

Como se puede ver, no hay nada obvio que puede hacer desde la llamada al método de __enter__() el administrador de contexto que puede omitir el cuerpo ("BLOCK") de la sentencia with.

La gente ha hecho cosas específicas de la implementación de Python, como la manipulación de la pila de llamadas dentro de __enter__(), en proyectos como withhacks. Recuerdo que Alex Martelli publicó un muy interesante hackeo en stackoverflow hace uno o dos años (no recuerdo suficiente de la publicación para buscar y encontrarla).

Pero la respuesta simple a su pregunta/problema es que no puede hacer lo que está preguntando, saltándose el cuerpo de la declaración con, sin recurrir a la llamada "magia profunda" (que no es necesariamente portátil entre python implementaciones). Con magia profunda, es posible que puedas hacerlo, pero te recomiendo hacer cosas como un ejercicio para ver cómo se puede hacer, nunca en un "código de producción".

+1

oh mi dios 'withhacks' es una locura, hicieron bloques de estilo Ruby en Python ... – nneonneo

+0

OK, esto explica mucho. Salí con chaquetas. Creo que en este momento me supera ... No estoy seguro de cómo puedo usar el código para realizar el salto, pero definitivamente hay fragmentos interesantes de códigos que podría usar. (Actualización: RUBY STYLE BLOCKS? Veo, jajaja. De hecho, es una locura) De lo contrario, realmente necesitaré pensar en otro enfoque entonces. ¡Gracias! –

Cuestiones relacionadas