2012-06-05 21 views
13

Tengo muchas líneas en una fila que pueden arrojar una excepción, pero no importa qué, todavía debe continuar la siguiente línea. ¿Cómo hacer esto sin intentar individualmente capturar cada afirmación que pueda arrojar una excepción?¿Cómo evitar intentar atrapar todas las líneas posibles en Python?

try: 
    this_may_cause_an_exception() 
    but_I_still_wanna_run_this() 
    and_this() 
    and_also_this() 
except Exception, e: 
    logging.exception('An error maybe occured in one of first occuring functions causing the others not to be executed. Locals: {locals}'.format(locals=locals())) 

Veamos el código anterior, todas las funciones pueden lanzar excepciones, pero todavía debe ejecutar las siguientes funciones no importa si se produjo una excepción o no. ¿Hay una buena manera de hacer eso?

No quiero hacer esto:

try: 
    this_may_cause_an_exception() 
except: 
    pass 
try: 
    but_I_still_wanna_run_this() 
except: 
    pass 
try: 
    and_this() 
except: 
    pass 
try: 
    and_also_this() 
except: 
    pass 

Creo código todavía debe continuar ejecutándose después de una excepción sólo si la excepción es crítica (El equipo se quemará o todo el sistema será llegar en mal estado, se debe detener todo el programa, pero para muchas cosas pequeñas también se lanzan excepciones como conexión fallida, etc.) Normalmente no tengo ningún problema con el manejo de excepciones, pero en este caso estoy usando una biblioteca de terceros que arroja fácilmente excepciones para cosas pequeñas

Después de ver la respuesta de m4spy, pensé que no sería posible, tener un decorador que permita que cada línea en la función se ejecute aunque una de ellas presente una excepción.

Algo como esto sería genial:

def silent_log_exceptions(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except Exception: 
      logging.exception('...') 
      some_special_python_keyword # which causes it to continue executing the next line 
    return _wrapper 

O algo así:

def silent_log_exceptions(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     for line in func(*args, **kwargs): 
      try: 
       exec line 
      except Exception: 
       logging.exception('...') 
    return _wrapper 



@silent_log_exceptions 
def save_tweets(): 
    a = requests.get('http://twitter.com) 
    x = parse(a) 
    bla = x * x 
+3

Ninguno de estos estilos de codificación de sentirse terriblemente Pythonic ... No estoy realmente seguro de lo que sugieren, pero espero con interés la respuesta. – Crisfole

Respuesta

15
for func in [this_may_cause_an_exception, 
      but_I_still_wanna_run_this, 
      and_this, 
      and_also_this]: 
    try: 
     func() 
    except: 
     pass 

Hay dos cosas a destacar aquí:

  • todas las acciones que querer realizar tener representado por callables con el misma firma (en el ejemplo, callables que no toman argumentos). Si aún no lo están, envuélvalos en funciones pequeñas, expresiones lambda, clases invocables, etc.
  • Las cláusulas except Bare son una mala idea, pero probablemente ya lo sabía.

Un enfoque alternativo, que es más flexible, es el uso de una función de orden superior como

def logging_exceptions(f, *args, **kwargs): 
    try: 
     f(*args, **kwargs) 
    except Exception as e: 
     print("Houston, we have a problem: {0}".format(e)) 
+0

Sí, lo sabía, por eso puse el primero con el registro de los lugareños en la pregunta. Es algo nuevo a lo que me acostumbré recientemente para iniciar sesión en mis locales, que ayuda mucho con la depuración de los servidores de producción. Me gusta mucho la forma en que se hace una lista de funciones, aunque en mi pregunta son todas las funciones en mi ejemplo de la vida real la mayoría de ellas son declaraciones y también variables de guardado. Pero podría solucionarlo. –

0
try: 
    this_may_cause_an_exception() 
except: 
    logging.exception('An error occured') 
finally: 
    but_I_still_wanna_run_this() 
    and_this() 
    and_also_this() 

Se puede utilizar el bloque de manejo de excepciones finally. En realidad, está destinado a código de limpieza.

EDIT: Veo que dijo que todas las funciones pueden arrojar excepciones, en cuyo caso la respuesta de larsmans es la más clara que puedo imaginar para detectar excepciones para cada llamada de función.

+3

¿Qué pasa si ocurre una excepción en but_i_still_wanna_run_this() – shiva

+0

? Por eso edité la publicación para incluir mi comprensión de que Sam quiere detectar excepciones en cada función. –

+1

Simplemente elimine su publicación si no aborda la pregunta correctamente ... – schlamar

2

Me encontré con algo similar, y hice una pregunta en SO here. La respuesta aceptada maneja el registro y observa solo una excepción específica.Terminé con una versión modificada:

class Suppressor: 
    def __init__(self, exception_type, l=None): 
     self._exception_type = exception_type 
     self.logger = logging.getLogger('Suppressor') 
     if l: 
      self.l = l 
     else: 
      self.l = {} 
    def __call__(self, expression): 
     try: 
      exec expression in self.l 
     except self._exception_type as e: 
      self.logger.debug('Suppressor: suppressed exception %s with content \'%s\'' % (type(self._exception_type), e)) 

utilizable como tal:

s = Suppressor(yourError, locals()) 
s(cmdString) 

por lo que podría configurar una lista de comandos y el uso de map con el supresor de correr a través de todos ellos.

+3

Nunca establezca un valor de parámetro predeterminado en una variable como 'Lista',' Diccionario' o (en algunas condiciones) 'objeto'. Porque se evalúan solo una vez y causarán pérdidas de memoria graves y errores lógicos. http://docs.python.org/tutorial/controlflow.html#default-argument-values ​​ – FallenAngel

+0

use un decorador en su lugar ... – schlamar

+0

@FallenAngel ¡Vaya! No puedo creer que lo haya hecho, ¡gracias! –

0

Puede manejar una tarea tan con un decorador:

import logging 
from functools import wraps 

def log_ex(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except Exception: 
      logging.exception('...') 
    return _wrapper 

@log_ex 
def this_may_cause_an_exception(): 
    print 'this_may_cause_an_exception' 
    raise RuntimeError() 

@log_ex 
def but_i_wanna_run_this(): 
    print 'but_i_wanna_run_this' 

def test(): 
    this_may_cause_an_exception() 
    but_i_wanna_run_this() 

Llamar a la función de prueba se verá así (que mostrará que ambas funciones se ejecutaron):

>>> test() 
this_may_cause_an_exception 
ERROR:root:... 
Traceback (most recent call last): 
    File "<stdin>", line 5, in _wrapper 
    File "<stdin>", line 4, in my_func 
RuntimeError 
but_i_wanna_run_this 
+0

¿Qué sucede si una línea en la función genera una excepción, no creo que ejecute la siguiente línea, ¿verdad? ¿Pero salga de la función y registre la excepción? –

+0

@SamStoelinga No, pero se llamará a la siguiente función.Actualicé la respuesta anterior, por lo que debería ser más clara. Es un enfoque similar al de la respuesta aceptada, pero no es necesario crear la lista y sus funciones no necesitan tener una firma fija. – schlamar

+0

Ah si eso fue una estupidez de mi parte;) también es una buena solución, pero aun así me causaría un montón de código, creo que la mejor solución sería tener un decorador, que permita ejecutar toda la función sin importar si alguna línea de la función provoca un error, aunque no estoy seguro si esto es posible en Python. –

0

A veces, cuando el lenguaje no admite su manera elegante de expresar una idea porque el desarrollo del lenguaje literalmente falló en las últimas décadas, solo puede confiar en el hecho de que Python sigue siendo un lenguaje dinámico que admite la instrucción exec, que hace posible lo siguiente:

code=""" 
for i in range(Square_Size): 
    Square[i,i] @= 1 
    Square[i+1,i] @= 2 
    @dowhatever() 
""" 

Este nuevo operador hace que el código sea más Pythonic y elegante, ya que no es necesario especificar si-statemens adicionales que garantizan que el índice se mantiene en cota o la función no tener éxito, que es totalmente irrelevante para lo que queremos express (simplemente no debe detenerse) aquí (nota: mientras que la indexación segura sería posible mediante la creación de una clase basada en la clase de lista, este operador funciona siempre que haya una captura de prueba), en Lisp sería fácil definirlo de manera Lispy, pero parece imposible definirlo de manera elegante en Python, pero aún así, aquí está el pequeño preprocesador que lo hará posible: exec "\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]]) #new <- hackish operator which wraps try catch into line

El resultado, suponiendo g que Square era 4x4 y contenía sólo ceros:

[1 0 0 0] 
[2 1 0 0] 
[0 2 1 0] 
[0 0 2 1] 

pertinentes: Sage/Sagemath CAS utiliza un preparse-función que transforma código antes de que llegue el intérprete Python. Un mono-parche para esa función sería:

def new_preparse(code,*args, **kwargs): 
    code="\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]]) 
    return preparse(code) 
sage.misc.preparser.preparse=new_preparse 
Cuestiones relacionadas