Excepciones en Python — try, except, raise y assert sin rodeos
Las excepciones en Python son el mecanismo que permite a un programa gestionar situaciones inesperadas sin romperse. En FP1 aprendiste a prevenir errores con if/else. En FP2 das un paso más, aprendes a capturarlos cuando ocurren y a lanzarlos tú mismo cuando es necesario.
En este artículo vemos todo: try/except, excepciones específicas y genéricas, finally, raise y assert.
Tabla de Contenidos
¿Qué son las excepciones en Python?
Cuando Python encuentra una situación que no puede manejar, dividir entre cero, convertir texto que no es número, acceder a un índice que no existe, lanza una excepción y el programa se detiene. Una excepción es un objeto que representa ese error.
a = int(input('a: '))
b = int(input('b: '))
print(a // b)
a: 7 b: 0 ZeroDivisionError: integer division or modulo by zero
El programa muestra el tipo de excepción (ZeroDivisionError) y termina. Sin gestión de excepciones, cualquier error inesperado mata el programa.
try/except vs if/else — cuándo usar cada uno
La pregunta que más aparece en FP2: ¿cuándo uso if/else y cuándo uso try/except?
# CON if/else — prevención
b = int(input('b: '))
if b != 0:
print(a // b)
else:
print('No se puede dividir entre cero')
# CON try/except — gestión
try:
b = int(input('b: '))
print(a // b)
except ZeroDivisionError:
print('No se puede dividir entre cero')
La regla práctica es clara:
Usa if/else cuando puedes comprobar la condición antes de que ocurra el error y la comprobación es barata, como comprobar si un número es cero antes de dividir.
Usa try/except cuando no puedes saber de antemano si va a ocurrir un error, como intentar abrir un fichero, convertir entrada del usuario, o acceder a recursos externos. También cuando el código que puede fallar es complejo y envolverlo en condiciones lo haría ilegible.
En Python hay además una filosofía conocida como EAFP (Easier to Ask Forgiveness than Permission): es más pythónico intentar la operación y capturar el error que comprobar antes si es posible hacerla.
La estructura try/except
try:
# código que puede lanzar una excepción
except TipoDeExcepcion:
# código que se ejecuta si ocurre esa excepción
Cuando ocurre una excepción dentro del try, Python salta inmediatamente al except correspondiente, las instrucciones que quedan en el try no se ejecutan:
try:
a = int(input('a: '))
b = int(input('b: '))
print(a // b) # (1)
print('Listo') # (2)
except ZeroDivisionError:
print('División por cero') # (3)
Si b es 0, ocurre la excepción en (1), (2) no se ejecuta y se pasa directamente a (3).
Except específico — capturar excepciones concretas
Lo más recomendable es capturar excepciones específicas, así sabes exactamente qué error ocurrió y puedes gestionarlo de forma apropiada:
try:
a = int(input('a: '))
b = int(input('b: '))
print(a // b)
except ValueError:
print('Introduce números enteros')
except ZeroDivisionError:
print('No se puede dividir entre cero')
Si el usuario introduce texto, ocurre ValueError, lo captura el primer except. Si introduce cero, ocurre ZeroDivisionError, lo captura el segundo. Cada excepción tiene su bloque de gestión.
Las excepciones más comunes en FP2:
ValueError # valor incorrecto para la operación (int('hola'))
ZeroDivisionError # división entre cero
TypeError # tipo de dato incorrecto
IndexError # índice fuera de rango en lista o cadena
KeyError # clave no encontrada en diccionario
FileNotFoundError # fichero no encontrado
AttributeError # atributo o método no existe en el objeto
Capturar varias excepciones en un mismo bloque
Si quieres gestionar varias excepciones de la misma manera, puedes agruparlas en una tupla:
try:
a = int(input('a: '))
b = int(input('b: '))
print(a // b)
except (ValueError, ZeroDivisionError):
print('Error en los datos de entrada')
Except genérico — la diferencia que más confunde
El except sin tipo captura cualquier excepción, es el equivalente al else final de un if/elif:
try:
a = int(input('a: '))
b = int(input('b: '))
print(a // b)
except ValueError:
print('Introduce números enteros')
except ZeroDivisionError:
print('No se puede dividir entre cero')
except:
print('Ocurrió un error inesperado')
El except genérico va siempre al final y captura cualquier excepción no contemplada antes. Úsalo con moderación, si capturas todo sin saber qué es, puedes ocultar errores reales en tu código.
Acceder a la información de la excepción
Puedes dar nombre al objeto excepción para acceder a su información:
try:
a = int(input('a: '))
b = int(input('b: '))
print(a // b)
except Exception as err:
print('Error:', err) # mensaje del error
print(type(err)) # tipo de excepción
print(err.args) # información adicional como tupla
a: 7
b: 0
Error: integer division or modulo by zero
<class 'ZeroDivisionError'>
('integer division or modulo by zero',)
Exception es la clase base de la que heredan todos los tipos de excepciones. Úsala cuando quieras capturar cualquier excepción pero necesites acceder a su información.
Los bloques adicionales — else y finally
Además de except, un bloque try puede tener else y finally:
try:
a = int(input('a: '))
b = int(input('b: '))
resultado = a // b
except ValueError:
print('Introduce números enteros')
except ZeroDivisionError:
print('No se puede dividir entre cero')
else:
print(resultado) # solo se ejecuta si NO hubo excepción
finally:
print('Ejecución terminada') # se ejecuta SIEMPRE
else — se ejecuta solo si el bloque try no lanzó ninguna excepción. Es el lugar para código que depende de que el try haya tenido éxito pero que no necesita estar protegido.
finally — se ejecuta siempre, haya o no excepción. Su uso principal es liberar recursos, cerrar ficheros, conexiones de red, bases de datos, que deben cerrarse pase lo que pase.
# Sin excepción: a: 10 / b: 2 → 5 Ejecución terminada # Con excepción: a: 10 / b: 0 → No se puede dividir entre cero Ejecución terminada
El finally es el bloque que más confunde al principio, recuerda: siempre se ejecuta, incluso si hay una excepción no capturada o un return dentro del try.
La sentencia raise — lanzar excepciones propias
Hasta ahora Python lanzaba las excepciones. Con raise puedes lanzarlas tú cuando detectas una situación anómala:
import math
def area_circulo(radio):
if radio < 0:
raise ValueError('El radio no puede ser negativo')
return math.pi * radio ** 2
try:
print(area_circulo(5)) # → 78.539...
print(area_circulo(-2)) # lanza ValueError
except ValueError as err:
print(err) # → El radio no puede ser negativo
raise acepta cualquier tipo de excepción con un mensaje descriptivo:
raise ValueError('mensaje descriptivo')
raise TypeError('tipo de dato incorrecto')
raise ZeroDivisionError('el denominador es cero')
raise ValueError # sin mensaje — usa el mensaje por defecto
raise sin argumentos — propagar excepciones
Dentro de un except puedes usar raise solo para propagar la excepción hacia el código que llamó a tu función:
def leer_numero():
intentos = 0
while True:
try:
return int(input('Número: '))
except ValueError:
intentos += 1
print('Introduce un entero')
if intentos >= 3:
raise # propaga el ValueError hacia fuera
try:
leer_numero()
except ValueError:
print('Demasiados errores — programa terminado')
Número: a Introduce un entero Número: b Introduce un entero Número: c Introduce un entero Demasiados errores — programa terminado
El raise sin argumentos dentro del except relanza la misma excepción que se está gestionando. Si nadie la captura, el programa termina.
Crear excepciones propias
Puedes definir tus propios tipos de excepción creando una clase que herede de Exception:
class ErrorRadioNegativo(Exception):
pass
class ErrorRadioTipo(Exception):
pass
import math
def area_circulo(radio):
if not (type(radio) == int or type(radio) == float):
raise ErrorRadioTipo('El radio debe ser un número')
if radio < 0:
raise ErrorRadioNegativo('El radio no puede ser negativo')
return math.pi * radio ** 2
try:
print(area_circulo('cinco'))
except ErrorRadioTipo as err:
print(err)
except ErrorRadioNegativo as err:
print(err)
El radio debe ser un número
Por convención los nombres de excepciones propias terminan en Error, igual que las predefinidas (ValueError, TypeError…). Una excepción propia puede ser tan simple como pass o incluir atributos adicionales con información sobre el error.
Asertos — assert
Un aserto es una condición que debe ser verdadera en un punto concreto del programa para que pueda continuar:
def media(elementos):
assert len(elementos) != 0, 'La lista no puede estar vacía'
return sum(elementos) / len(elementos)
print(media([1, 2, 3])) # → 2.0
print(media([])) # → AssertionError: La lista no puede estar vacía
assert condicion, mensaje, si la condición es falsa lanza AssertionError con el mensaje. El mensaje es opcional.
Los asertos son equivalentes a:
if __debug__:
if len(elementos) == 0:
raise AssertionError('La lista no puede estar vacía')
Cuándo usar assert vs raise:
# USA assert para — condiciones que nunca deberían ocurrir
# si el programa está correctamente escrito (errores de programación)
assert len(lista) > 0
# USA raise para — situaciones anómalas esperables en producción
# que el usuario o el entorno pueden provocar
if radio < 0:
raise ValueError('Radio negativo')
Los asertos pueden desactivarse ejecutando Python con la opción -O (optimización). Por eso nunca los uses para validar entrada del usuario, usa raise para eso.
Visualízalo con Python Tutor
Copia este código en pythontutor.com:
def dividir(a, b):
if b == 0:
raise ZeroDivisionError('No se puede dividir entre cero')
return a / b
try:
resultado = dividir(10, 0)
except ZeroDivisionError as err:
print(err)
finally:
print('Siempre se ejecuta')
Ejecuta paso a paso y observa cómo el raise interrumpe la función y el flujo salta directamente al except, y cómo el finally se ejecuta en cualquier caso.
Resumen rápido
# ESTRUCTURA BÁSICA
try:
# código que puede fallar
except TipoEspecifico:
# gestión específica
except (Tipo1, Tipo2):
# varios tipos juntos
except Exception as err:
# cualquier excepción + acceso a info
except:
# cualquier excepción (sin info)
else:
# solo si NO hubo excepción
finally:
# SIEMPRE se ejecuta
# RAISE
raise ValueError('mensaje') # lanzar excepción
raise # propagar excepción actual
# EXCEPCIÓN PROPIA
class MiError(Exception):
pass
raise MiError('mensaje')
# ASSERT
assert condicion, 'mensaje' # AssertionError si falsa
# try/except vs if/else
# if/else → prevenir errores conocidos y comprobables
# try/except → gestionar errores inesperados o en recursos externos
# EXCEPCIONES COMUNES
# ValueError — valor incorrecto
# ZeroDivisionError — división entre cero
# TypeError — tipo incorrecto
# IndexError — índice fuera de rango
# KeyError — clave no encontrada
# FileNotFoundError — fichero no existe
En el próximo artículo practicamos las excepciones con programas reales, validadores robustos, gestión de ficheros y excepciones propias en acción.
Siguiente paso: prácticaExcepciones en Python — 4 programas reales que no se rompen¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Un comentario