una - tipos de excepción en python
Generar excepciones cuando ya existe una excepción en Python 3 (5)
¿Qué sucede con mi primera excepción ( A
) cuando la segunda ( B
) aparece en el siguiente código?
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
obtengo:
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
obtengo:
second
Preguntas
- ¿A dónde fue mi primera excepción?
- ¿Por qué solo se puede capturar la excepción más externa?
- ¿Cómo puedo quitar la excepción más externa y volver a subir las excepciones anteriores?
Actualización0
Esta pregunta aborda específicamente Python 3, ya que su manejo de excepciones es bastante diferente al de Python 2.
- Fue echado fuera.
- Solo una excepción puede estar "activa" a la vez por subproceso.
- No puede, a menos que encapsule la excepción anterior en la excepción posterior de alguna manera.
Creo que todos los ingredientes para responder a sus preguntas ya están en las respuestas existentes. Déjame combinar y elaborar.
Permítame repetir el código de su pregunta para proporcionar referencias de números 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:
- ¿A dónde fue mi primera excepción?
Su primera excepción A
aparece en la línea 6. La cláusula finally
en la línea 7 siempre se ejecuta tan pronto como se deja el bloque try
(líneas 5-6), independientemente de si se deja debido a la finalización exitosa o debido a una excepción elevada. Mientras se ejecuta la cláusula finally
, la línea 8 genera otra excepción B
Como lo han señalado Lennart e Ignazio, solo se puede hacer un seguimiento de una excepción, la que se planteó más recientemente. Entonces, tan pronto como se levanta B
, el bloque try
general (líneas 4-8) se cierra y la excepción B
está siendo capturada por la instrucción de except
en la línea 9 si coincide (si X
es B
).
- ¿Por qué solo se puede capturar la excepción más externa?
Esperemos que esto quede claro ahora por mi explicación de 1. Sin embargo, podría detectar la excepción interna / inferior / primera. Para unirte a la respuesta de Lennart, ligeramente modificada, aquí tienes cómo atrapar a los dos:
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'',))
- ¿Cómo puedo quitar la excepción más externa y volver a subir las excepciones anteriores?
En el ejemplo de Lennart, la solución a esta pregunta es la línea, except A as e
donde la excepción interna / inferior / primera se captura y almacena en la variable e
.
Como una sensación general de cuándo atrapar excepciones, cuándo ignorarlas y cuándo volver a plantearlas, tal vez esta pregunta y la respuesta de Alex Martelli ayuden.
El manejo de excepciones de Pythons solo se ocupará de una excepción a la vez. Sin embargo, los objetos de excepción están sujetos a las mismas reglas variables y recolección de basura que todo lo demás. Por lo tanto, si guarda el objeto de excepción en una variable en algún lugar, puede tratar con él más tarde, incluso si se produce otra excepción.
En su caso, cuando se produce una excepción durante la declaración "finalmente", Python 3 imprimirá el rastreo de la primera excepción antes de la de la segunda excepción, para que sea 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 siguiente 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 ve, ahora puede acceder a la excepción original.
La excepción ''causante'' está disponible como c .__ context__ en su último controlador de excepciones. Python está utilizando esta información para hacer un seguimiento más útil. Bajo Python 2.x, la excepción original se habría perdido, esto es solo para Python 3.
Por lo general, usaría esto para lanzar una excepción consistente mientras mantiene la excepción original accesible (aunque es bastante bueno que ocurra automáticamente desde un controlador de excepciones, ¡no lo sabía!):
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
Respondiendo a la pregunta 3, puedes usar:
raise B(''second'') from None
Lo 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