2009-10-06 7 views
59

Tengo una lista de comprensión en Python en la que cada iteración puede arrojar una excepción.¿Cómo puedo manejar excepciones en una lista de comprensión en Python?

Por ejemplo, si tengo:

eggs = (1,3,0,3,2) 

[1/egg for egg in eggs] 

que obtendrá una excepción de ZeroDivisionError en el 3er elemento.

¿Cómo puedo manejar esta excepción y continuar la ejecución de la lista de comprensión?

La única manera que se me ocurre es utilizar una función auxiliar:

def spam(egg): 
    try: 
     return 1/egg 
    except ZeroDivisionError: 
     # handle division by zero error 
     # leave empty for now 
     pass 

Pero esto parece un poco complicado para mí.

¿Existe alguna forma mejor de hacerlo en Python?

Nota: Este es un ejemplo sencillo (ver "por ejemplo" más arriba) que ingenié porque mi ejemplo real requiere un poco de contexto. No estoy interesado en evitar dividir por cero errores, pero en el manejo de excepciones en una lista de comprensión.

+2

Hay una [PEP 463] (https://www.python.org/dev/peps/pep-0463/) para añadir una expresión de manejar excepciones. En su ejemplo, sería '[1/egg excepto ZeroDivisionError: None para egg in (1,3,0,3,2)]'. Pero todavía está en modo borrador. Mi instinto es que no va a ser aceptado. Las expresiones de Imho pueden volverse demasiado complicadas (ver múltiples excepciones, tener combinaciones más complejas (operadores lógicos múltiples, comprensiones complejas, etc.) – cfi

+1

Tenga en cuenta que para este ejemplo * específico *, podría usar un numpy 'ndarray' con las configuraciones apropiadas en' np. seterr'. Eso daría como resultado '1/0 = nan'. Pero me doy cuenta de que no se generaliza a otras situaciones donde surge esta necesidad. – gerrit

Respuesta

55

No hay expresión incorporada en Python que le permite ignorar una excepción (o devolver valores alternativos & c en caso de excepciones), por lo que es imposible, literalmente, "manejar excepciones en una lista de comprensión" porque una lista de comprensión es una expresión que contiene expresión, nada más (es decir, no declaraciones, y solo las declaraciones pueden capturar/ignorar/manejar excepciones).

Las llamadas a funciones son expresiones y los cuerpos de funciones pueden incluir todas las declaraciones que desee, por lo que delegar la evaluación de la subexpresión propensa a excepciones a una función, como habrá notado, es una solución factible (otros, cuando sea factible, se verifican los valores que pueden provocar excepciones, como también se sugiere en otras respuestas).

Las respuestas correctas a la pregunta "cómo manejar excepciones en una lista de comprensión" están expresando parte de toda esta verdad: 1) literalmente, es decir, léxicamente EN la comprensión misma, no se puede; 2) Prácticamente, delega el trabajo a una función o verifica si hay valores propensos a errores cuando eso es factible. Por lo tanto, su afirmación repetida de que esta no es una respuesta es infundada.

+6

Ya veo. Entonces, una respuesta completa sería que yo debería: 1. usar una función 2. no usar la lista de comprensión 3. tratar de evitar la excepción en lugar de manejarla. –

+8

No veo "no usar listas de comprensión" como parte de la respuesta a "cómo manejar excepciones en listas de comprensión", pero creo que podría razonablemente verlo como una posible consecuencia de "_lexically IN_ the LC, no es posible manejar excepciones ", que es de hecho la primera parte de la respuesta literal. –

+0

¿Se puede detectar un error en la expresión de un generador o en la comprensión del generador? – Steve

18

Puede utilizar

[1/egg for egg in eggs if egg != 0] 

esto se puede omitir elementos que son cero.

+10

Esto no responde a la pregunta de cómo manejar excepciones en una lista de comprensión. –

+4

umm, sí, lo hace, obvia la necesidad de manejar excepciones. Sí, no es la solución correcta todo el tiempo, pero es común. – Peter

+1

Entiendo. Retiro el comentario (aunque no lo eliminaré) ya que esa breve 'discusión' mejora la respuesta). –

8

No, no hay una mejor manera. En muchos casos, puede utilizar la evitación como Pedro hace

Su otra opción es no utilizar comprensiones

eggs = (1,3,0,3,2) 

result=[] 
for egg in eggs: 
    try: 
     result.append(egg/0) 
    except ZeroDivisionError: 
     # handle division by zero error 
     # leave empty for now 
     pass 

Hasta que decidir si eso es más complicado o no

+0

¿Cómo usaría las comprensiones aquí? –

+0

@Nathan: no lo haría. gnibbler dice: * No, no hay una manera mejor * – SilentGhost

+0

Lo siento ... Me perdí el 'no' en su respuesta :-) –

58

que dan cuenta de esta pregunta es bastante antiguo, pero también se puede crear una función general para hacer este tipo de cosas más fáciles:

def catch(func, handle=lambda e : e, *args, **kwargs): 
    try: 
     return func(*args, **kwargs) 
    except Exception as e: 
     return handle(e) 

Luego, en su comprensión:

eggs = (1,3,0,3,2) 
[catch(lambda : 1/egg) for egg in eggs] 
[1, 0, ('integer division or modulo by zero'), 0, 0] 

Puede por supuesto, haga que el manejador por defecto funcione como prefiera (digamos que prefiere devolver 'None' por defecto).

Espero que esto lo ayude a usted o a cualquier espectador futuro de esta pregunta.

Nota: en Python 3, que haría que el argumento de palabra clave 'mango' solamente, y lo puso al final de la lista de argumentos. Esto haría que pasar los argumentos y tal a través de la captura sea mucho más natural.

+1

extremadamente útil, gracias. Si bien estoy de acuerdo con los comentarios teóricos, esto muestra un enfoque práctico para resolver un problema que he tenido repetidamente. – Paul

+1

Greate respuesta. Un mod que sugiero es pasar 'args' y' kwargs' para manejarlo también. De esta forma, podría devolver 'egg' en lugar de' 0' codificado, o una excepción como lo está haciendo. –

+1

Es posible que también desee el tipo de excepción como argumento opcional (¿pueden parametrizarse los tipos de excepción?), De modo que las excepciones inesperadas se lanzan hacia arriba en lugar de ignorar todas las excepciones. – 00prometheus

1

creo una función de ayuda, según lo sugerido por el que hace la pregunta inicial y Bryan cabeza, así, es bueno y no engorroso en absoluto. Una sola línea de código mágico que hace todo el trabajo no siempre es posible, por lo que una función auxiliar es una solución perfecta si se quiere evitar los bucles for. Sin embargo, me gustaría modificarlo para que éste:

# A modified version of the helper function by the Question starter 
def spam(egg): 
    try: 
     return 1/egg, None 
    except ZeroDivisionError as err: 
     # handle division by zero error   
     return None, err 

La salida será este [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. Con esta respuesta, tienes el control total para continuar de la manera que quieras.

Cuestiones relacionadas