Excepciones múltiples y jerarquía
Introducción
Cuando trabajamos con código Python, es común encontrarnos con diferentes tipos de errores que pueden interrumpir la ejecución de nuestro programa. En el artículo anterior, aprendimos cómo capturar estas excepciones usando bloques try-except
. Sin embargo, en aplicaciones reales, a menudo necesitamos gestionar múltiples tipos de excepciones de forma diferente. En este artículo, exploraremos cómo manejar excepciones múltiples y entenderemos la jerarquía de excepciones en Python, lo que nos permitirá crear código más robusto y con mejor manejo de errores.
Capturando múltiples excepciones
Excepciones múltiples en bloques separados
La forma más básica de manejar varios tipos de excepciones es utilizando múltiples bloques except
:
try:
numero = int(input("Introduce un número: "))
resultado = 100 / numero
print(f"El resultado es: {resultado}")
except ValueError:
print("Error: Debes introducir un número válido.")
except ZeroDivisionError:
print("Error: No se puede dividir entre cero.")
En este ejemplo:
- Si el usuario introduce algo que no es un número, se lanzará un
ValueError
y se ejecutará el primer bloqueexcept
. - Si el usuario introduce un cero, se lanzará un
ZeroDivisionError
y se ejecutará el segundo bloqueexcept
.
Agrupando excepciones
También podemos agrupar múltiples excepciones en un solo bloque except
cuando queremos manejarlas de la misma manera:
try:
numero = int(input("Introduce un número: "))
resultado = 100 / numero
print(f"El resultado es: {resultado}")
except (ValueError, ZeroDivisionError):
print("Error: Debes introducir un número válido que no sea cero.")
Capturando la información de la excepción
Es posible acceder a la información de la excepción utilizando la cláusula as
:
try:
archivo = open("archivo_inexistente.txt", "r")
contenido = archivo.read()
archivo.close()
except FileNotFoundError as error:
print(f"Error al abrir el archivo: {error}")
print(f"Tipo de error: {type(error)}")
La variable error
contiene toda la información de la excepción, lo que nos permite acceder a sus detalles y mostrar mensajes más informativos.
Jerarquía de excepciones
Las excepciones en Python están organizadas en una jerarquía de clases. Todas las excepciones heredan de la clase base BaseException
, aunque la mayoría de las excepciones que utilizaremos heredan de Exception
.
Estructura básica de la jerarquía
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── StopIteration
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
Implicaciones de la jerarquía
La jerarquía de excepciones es importante porque cuando capturamos una excepción de un tipo determinado, también capturaremos todas las excepciones que heredan de ese tipo. Por ejemplo:
try:
# Código que puede generar diferentes tipos de LookupError
lista = [1, 2, 3]
print(lista[10]) # IndexError (subclase de LookupError)
diccionario = {"clave": "valor"}
print(diccionario["otra_clave"]) # KeyError (subclase de LookupError)
except LookupError as error:
print(f"Error de búsqueda: {error}")
En este código, capturamos tanto IndexError
como KeyError
porque ambos son subclases de LookupError
.
El orden importa
Es importante tener en cuenta el orden de los bloques except
cuando manejamos excepciones en diferentes niveles de la jerarquía:
try:
# Código que puede lanzar excepciones
resultado = 10 / 0
except Exception as e:
print(f"Se ha capturado una excepción: {e}")
except ZeroDivisionError as e:
print(f"División por cero: {e}")
En este ejemplo, el bloque except ZeroDivisionError
nunca se ejecutará porque Exception
es una clase base de ZeroDivisionError
, y las excepciones se manejan en el orden en que aparecen los bloques except
. Siempre debemos colocar las excepciones más específicas antes que las más generales:
try:
# Código que puede lanzar excepciones
resultado = 10 / 0
except ZeroDivisionError as e:
print(f"División por cero: {e}")
except Exception as e:
print(f"Se ha capturado una excepción: {e}")
Ejemplo práctico de uso de la jerarquía
Un uso común de la jerarquía de excepciones es proporcionar un manejo específico para ciertos tipos de errores, con un manejo más genérico para otros:
def leer_configuracion(nombre_archivo):
try:
with open(nombre_archivo, 'r') as archivo:
lineas = archivo.readlines()
# Procesar configuración
return lineas
except FileNotFoundError:
print(f"El archivo {nombre_archivo} no existe. Usando configuración por defecto.")
return []
except PermissionError:
print(f"No tienes permisos para leer {nombre_archivo}.")
return None
except OSError as e:
print(f"Error del sistema al acceder al archivo: {e}")
return None
except Exception as e:
print(f"Error inesperado: {e}")
return None
# Probamos con diferentes escenarios
configuracion = leer_configuracion("config.txt")
En este ejemplo:
- Manejamos específicamente
FileNotFoundError
yPermissionError
. - Luego manejamos cualquier otro
OSError
(que es la clase base de ambos errores anteriores, pero capturará otros errores de sistema operativo). - Finalmente, capturamos cualquier otra excepción con
Exception
.
Resumen
En este artículo, hemos aprendido cómo manejar múltiples excepciones en Python y hemos explorado la jerarquía de excepciones. Ahora sabemos cómo capturar diferentes tipos de errores, agruparlos cuando sea necesario y acceder a la información detallada de cada excepción. También hemos comprendido la importancia del orden al capturar excepciones de diferentes niveles en la jerarquía. Estos conocimientos nos permitirán crear código más robusto que pueda manejar situaciones de error de manera elegante y específica. En el próximo artículo, exploraremos las cláusulas else
y finally
, que complementan el manejo de excepciones en Python.