Propagación
La propagación de excepciones en Python se refiere al comportamiento de las excepciones cuando estas no son capturadas en un bloque de código y "se propagan" hacia otros bloques, hasta que encuentran un try-except que las capture, o hasta que llegan al nivel más alto del programa, lo que genera un mensaje de error y finaliza la ejecución del programa.
-
Generación de una excepción:
Una excepción ocurre cuando se detecta un error durante la ejecución del código. Por ejemplo, dividir por cero o intentar acceder a un índice fuera de rango genera una excepción.
x = 1 / 0 # Esto genera una excepción -
Búsqueda de un bloque
try-except:Cuando ocurre una excepción, Python busca un bloque
try-excepten el código actual que pueda manejar ese tipo de excepción. Si encuentra uno, la ejecución salta al bloqueexceptcorrespondiente y el resto del bloquetryse omite.try:
x = 1 / 0
except:
print("¡No se puede dividir por cero!")En este caso, la excepción es capturada y manejada correctamente, imprimiendo el mensaje
"¡No se puede dividir por cero!". -
Propagación de la excepción:
Si la excepción no se captura en el bloque
try-exceptactual, la excepción se propaga hacia afuera, buscando en el siguiente nivel de la pila de llamadas (otro bloquetry-excepten funciones que llamaron a la función actual) hasta que encuentra un manejador que pueda capturarla.def funcion_b():
x = 1 / 0 # Excepción aquí
def funcion_a():
funcion_b() # Llama a funcion_b
try:
funcion_a() # Llama a funcion_a
except:
print("¡Capturada en la función principal!")Aquí, la excepción se produce en
funcion_by, como no hay un manejador para capturarla allí, la excepción se propaga haciafuncion_ay, finalmente, hasta el bloquetry-excepten la función principal, donde es capturada y manejada. -
Excepción no capturada:
Si la excepción no se captura en ningún nivel de la pila de llamadas, llega al nivel más alto del programa. En este caso, Python mostrará un mensaje de error (el traceback) y finalizará la ejecución del programa.
def funcion_b():
x = 1 / 0
def funcion_a():
funcion_b()
funcion_a() # Sin bloque try-except
# Resultado:
# ZeroDivisionError: division by zeroEn este caso, como no hay ningún bloque
try-except, la excepción no se captura y el programa termina con un error.
Ejemplos
Tenemos el siguiente programa:
def funcion_b():
x = 1 / 0
return x
def funcion_a():
a = funcion_b()
return a
var = funcion_a()
print(var)
Como vimos, al no capturar la excepción, se provoca un error en la ejecución del programa. Por lo tanto debemos capturar la excepción. Tenemos varios lugares donde capturarla. Por ejemplo, capturémosla en el programa principal:
def funcion_b():
x = 1 / 0
return x
def funcion_a():
a = funcion_b()
return a
try:
var = funcion_a()
except:
var = 1
print(var)
En este caso, la instrucción x = 1 / 0 provoca una excepción. Al no capturarse, esta se propaga a la función que llamó a esta función, en este caso funcion_a() en la instrucción a = funcion_b(). Esta línea tampoco es capturada por una excepción, entonces se propaga a la función que llamó a esta, que en este caso es el programa principal. Aquí sí que se captura, y por lo tanto se ejecuta el bloque de código del except y el programa imprimirá el valor 1.
Pero podemos capturar la excepción en otro lugar, como puede ser en la funcion_a():
def funcion_b():
x = 1 / 0
return x
def funcion_a():
try:
a = funcion_b()
except:
a = 2
return a
var = funcion_a()
print(var)
En este caso, la instrucción x = 1 / 0 provoca una excepción. Al no capturarse, esta se propaga a la función que llamó a esta función, en este caso funcion_a() en la instrucción a = funcion_b(). En esta línea se captura la excepción, por lo que a la variable a se le asigna el valor 2. El programa continúa su ejecución normal y por lo tanto imprimirá el valor 2.
También es posible capturar la excepción en la funcion_b() donde se provoca:
def funcion_b():
try:
x = 1 / 0
except:
x = 3
return x
def funcion_a():
a = funcion_b()
return a
var = funcion_a()
print(var)
Del mismo modo que en los casos anteriores x = 1 / 0 provoca una excepción, pero en este caso ya se captura en la propia funcion_b(), por lo que esta función devuelve el valor 3. El programa continúa su funcionamiento normal, por lo que se imprimirá el valor 3.
Pero, ¿qué pasa si intentamos capturar una excepción varias veces? Pues no es posible, porque una excepción sólo se puede capturar una única vez.
Supongamos este ejemplo que es una mezcla de los anteriores:
def funcion_b():
try:
x = 1 / 0
except:
x = 3
return x
def funcion_a():
try:
a = funcion_b()
except:
a = 2
return a
try:
var = funcion_a()
except:
var = 1
print(var)
La excepción provocada por la instrucción x = 1 / 0 ya es capturada en la propia funcion_b, por lo que los bloques try de funcion_a y del programa principal no capturarán esta excepción. Por lo tanto, el programa imprimirá el valor 3.