2011-06-08 17 views
26

¿Qué ocurre con mi primera excepción (A) cuando aparece el segundo (B) en el siguiente código?Planteando excepciones cuando ya existe una excepción en Python 3

class A(Exception): pass 
class B(Exception): pass 

try: 
    try: 
     raise A('first') 
    finally: 
     raise B('second') 
except X as c: 
    print(c) 

Si se ejecuta con X = A me sale:

Traceback (most recent call last): 
    File "raising_more_exceptions.py", line 6, in 
    raise A('first') 
__main__.A: first 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
    File "raising_more_exceptions.py", line 8, in 
    raise B('second') 
__main__.B: second

Pero si X = B me sale:

second

Preguntas

  1. dónde se fue mi primera excepción?
  2. ¿Por qué solo se puede atrapar la excepción más externa?
  3. ¿Cómo elimino la excepción más externa y resubico las excepciones anteriores?

Update0

Esta pregunta se refiere específicamente a Python 3, ya que su manejo de excepciones es bastante diferente a Python 2.

+0

Las respuestas parecen estar descuidando el hecho de que todavía estoy obteniendo un rastreo completo cuando no se detecta la excepción. ¿Por favor explique? –

Respuesta

6
  1. Se echaron.
  2. Solo una excepción puede estar "activa" a la vez por subproceso.
  3. No se puede, a menos que encapsule la excepción anterior en la excepción posterior de alguna manera.
+1

Si se descartó, ¿por qué recibo un rastreo completo cuando no se detecta la excepción? –

+0

Porque la herramienta que está utilizando para ejecutar el script ha instalado un controlador global de excepciones y ha notado la doble excepción. –

+1

¿Qué herramienta .....? –

8

El manejo de excepciones de Pitones solo se ocupa de una excepción a la vez. Sin embargo, los objetos de excepción están sujetos a las mismas reglas de variables y a la recolección de elementos no utilizados que cualquier otra cosa. Por lo tanto, si guarda el objeto de excepción en alguna parte de una variable, puede tratarlo más tarde, incluso si se produce otra excepción.

En su caso, cuando se produce una excepción durante la instrucción "finally", Python 3 imprimirá la trazabilidad de la primera excepción antes de la de la segunda excepción, para ser más útil.

Un caso más común es que desea generar una excepción durante un manejo de excepciones explícito. Luego puede "guardar" la excepción en la próxima excepción. Solo pásalo como parámetro:

>>> class A(Exception): 
...  pass 
... 
>>> class B(Exception): 
...  pass 
... 
>>> try: 
...  try: 
...   raise A('first') 
...  except A as e: 
...   raise B('second', e) 
... except Exception as c: 
...  print(c.args[1]) 
... 
first 

Como puedes ver ahora puedes acceder a la excepción original.

+0

¿Puedes responder la parte 1? –

+0

@Matt: no va a ninguna parte. Sin embargo, me doy cuenta de que tuve un brainfart y actualicé mi respuesta. –

+0

¿Cómo maneja esta situación en doctests? – hayavuk

5

Creo que todos los ingredientes para responder a su (s) pregunta (s) ya están en las respuestas existentes. Déjame combinar y elaborar.

Permítanme repetir el código de la cuestión de proporcionar referencias de número de línea:

1 class A(Exception): pass 
2 class B(Exception): pass 
3 
4 try: 
5  try: 
6   raise A('first') 
7  finally: 
8   raise B('second') 
9 except X as c: 
10  print(c) 

Así que para responder a sus preguntas:

  1. dónde se fue mi primera excepción?

Su primera excepción A se plantea en la línea 6.La cláusula finally en la línea 7 es siempre ejecutada tan pronto como queda el bloque try (líneas 5-6), independientemente de si se deja debido a una finalización exitosa o debido a una excepción planteada. Mientras se ejecuta la cláusula finally, la línea 8 plantea otra excepción B. Como han señalado Lennart e Ignazio, solo se puede rastrear una excepción, la más reciente. Tan pronto como se levanta B, el bloque general try (líneas 4-8) se cierra y la excepción B es atrapada por la declaración except en la línea 9 si coincide (si X es B).

  1. ¿Por qué solo se puede atrapar la excepción más externa?

Afortunadamente, esto está claro ahora en mi explicación de 1. Sin embargo, podría detectar la excepción interna/inferior/primera. Para combinar en la respuesta de Lennart, ligeramente modificada, aquí es cómo atrapar tanto:

class A(Exception): pass 
class B(Exception): pass 
try: 
    try: 
     raise A('first') 
    except A as e: 
     raise B('second', e) 
except Exception as c: 
    print(c) 

la salida es:

('second', A('first',)) 
  1. ¿Cómo se despegue la excepción más externa y resubir las excepciones anteriores?

En el ejemplo de Lennart la solución a esta pregunta es la línea de except A as e, donde la parte inferior primera excepción interna// está siendo capturado y almacenado en la variable e.

Como una sensación general de cuándo detectar excepciones, cuándo ignorarlas, y cuándo volver a subir, tal vez this question and Alex Martelli's answer ayuda.

7

La excepción 'causar' está disponible como c .__ context__ en su último manejador de excepciones. Python está usando esta información para renderizar un rastreo más útil. En Python 2.x la excepción original se habría perdido, esto es solo para Python 3.

Normalmente se usaría esto para lanzar una excepción constante mientras se mantiene la excepción original accesible (aunque es muy bueno que se hace automáticamente desde un gestor de excepciones, no sabía eso!):

try: 
    do_something_involving_http() 
except (URLError, socket.timeout) as ex: 
    raise MyError('Network error') from ex 

Más información (y algunas otras cosas muy útiles que puedes hacer) aquí: http://docs.python.org/3.3/library/exceptions.html

3

Contestar a la pregunta 3, se puede utilizar:

raise B('second') from None 

que eliminará la excepción A traceback.

Traceback (most recent call last): 
    File "raising_more_exceptions.py", line 8, in 
    raise B('second') 
__main__.B: second 
Cuestiones relacionadas