Curso Python #9: Manejo de Errores y Excepciones
Manejo de Errores y Excepciones - Parte 9 de 10

Bienvenido al articulo 9 de 10 del Curso de Python desde Cero. Hasta ahora hemos aprendido a crear programas cada vez mas complejos: funciones, listas, diccionarios, clases, archivos... Pero hay un problema que no hemos enfrentado directamente: ¿que pasa cuando algo sale mal?
Imagina que tu programa le pide al usuario un numero y el usuario escribe "hola". O que intentas abrir un archivo que no existe. O que tratas de dividir entre cero. En todos estos casos, Python lanzara un error y tu programa se detendra abruptamente.
En este articulo vas a aprender a manejar errores para que tu programa no se rompa, sino que reaccione de forma inteligente cuando algo inesperado suceda. Esta es una habilidad fundamental para cualquier programador profesional.
¿Que son los errores en Python?
Un error es simplemente algo que sale mal cuando Python intenta ejecutar tu codigo. Hay dos grandes categorias de errores:
Errores de sintaxis (SyntaxError)
Estos errores ocurren cuando escribes codigo que Python no puede entender. Es como escribir una frase sin sentido en espanol — el interprete no sabe que hacer con ella.
1# Error de sintaxis: falta cerrar el parentesis
2print("Hola"
3
4# Error de sintaxis: falta los dos puntos
5if True
6 print("Verdadero")
7
8# Error de sintaxis: mala indentacion
9def saludar():
10print("Hola") # Falta la indentacion
Los errores de sintaxis se detectan antes de que el programa se ejecute. Python lee tu codigo, encuentra algo que no entiende y te muestra un mensaje de error indicando la linea del problema.
Excepciones (errores en tiempo de ejecucion)
Estos errores ocurren mientras el programa se esta ejecutando. La sintaxis es correcta, pero algo falla durante la ejecucion.
1# TypeError: intentar sumar un numero con un texto
2resultado = 5 + "hola"
3
4# ValueError: intentar convertir texto a numero
5numero = int("abc")
6
7# ZeroDivisionError: dividir entre cero
8resultado = 10 / 0
9
10# FileNotFoundError: abrir un archivo que no existe
11archivo = open("no_existe.txt")
12
13# IndexError: acceder a un indice que no existe
14lista = [1, 2, 3]
15print(lista[10])
16
17# KeyError: buscar una clave que no existe en un diccionario
18datos = {"nombre": "Ana"}
19print(datos["edad"])
Cada uno de estos errores tiene un nombre especifico (TypeError, ValueError, etc.) que te indica exactamente que tipo de problema ocurrio.
El bloque try/except: atrapar errores
El bloque try/except es la herramienta principal para manejar errores en Python. La idea es simple: "intenta ejecutar este codigo, y si falla, haz esto otro en lugar de detenerte".
Sintaxis basica
1try:
2 # Codigo que podria fallar
3 numero = int(input("Escribe un numero: "))
4 print(f"Tu numero es: {numero}")
5except:
6 # Que hacer si algo falla
7 print("Eso no es un numero valido")
Veamos como funciona paso a paso:
- Python intenta ejecutar el codigo dentro de
try - Si todo va bien, el bloque
exceptse ignora completamente - Si ocurre un error, Python deja de ejecutar el
tryy salta al bloqueexcept - Despues del
except, el programa continua normalmente
Ejemplo practico: calculadora segura
1# Sin manejo de errores - el programa se rompe
2dividendo = int(input("Dividendo: ")) # Si escribes "abc", CRASH
3divisor = int(input("Divisor: ")) # Si escribes 0, CRASH
4resultado = dividendo / divisor
5print(f"Resultado: {resultado}")
6
7# Con manejo de errores - el programa es robusto
8try:
9 dividendo = int(input("Dividendo: "))
10 divisor = int(input("Divisor: "))
11 resultado = dividendo / divisor
12 print(f"Resultado: {resultado}")
13except:
14 print("Ocurrio un error. Verifica los datos ingresados.")
Con el try/except, si el usuario escribe algo incorrecto, el programa muestra un mensaje amigable en lugar de romperse.
Atrapar excepciones especificas
Usar un except generico (sin especificar el tipo de error) funciona, pero no es recomendable. ¿Por que? Porque no sabes que error ocurrio, y no puedes dar un mensaje util al usuario.
Es mucho mejor atrapar excepciones especificas:
1try:
2 dividendo = int(input("Dividendo: "))
3 divisor = int(input("Divisor: "))
4 resultado = dividendo / divisor
5 print(f"Resultado: {resultado}")
6except ValueError:
7 print("Error: debes escribir numeros enteros, no letras.")
8except ZeroDivisionError:
9 print("Error: no puedes dividir entre cero.")
10except Exception as e:
11 print(f"Error inesperado: {e}")
Ahora el programa da un mensaje diferente segun el tipo de error. Esto es mucho mas util para el usuario.
Capturar el mensaje de error
Puedes guardar el error en una variable usando as para acceder a su mensaje:
1try:
2 numero = int("hola")
3except ValueError as error:
4 print(f"Ocurrio un error: {error}")
5 # Imprime: Ocurrio un error: invalid literal for int() with base 10: 'hola'
Excepciones mas comunes
| Excepcion | Cuando ocurre | Ejemplo |
|---|---|---|
ValueError |
Valor incorrecto para la operacion | int("abc") |
TypeError |
Tipo de dato incorrecto | "hola" + 5 |
ZeroDivisionError |
Division entre cero | 10 / 0 |
FileNotFoundError |
Archivo no encontrado | open("noexiste.txt") |
IndexError |
Indice fuera de rango | [1,2][5] |
KeyError |
Clave no existe en diccionario | {"a":1}["b"] |
AttributeError |
Atributo o metodo no existe | "hola".append("x") |
ImportError |
No se puede importar un modulo | import noexiste |
Los bloques else y finally
Ademas de try y except, Python tiene dos bloques adicionales que hacen el manejo de errores mas completo: else y finally.
El bloque else
El bloque else se ejecuta solo si NO hubo ningun error en el try. Es util para separar el codigo que podria fallar del codigo que depende de que todo salga bien.
1try:
2 numero = int(input("Escribe un numero: "))
3except ValueError:
4 print("Eso no es un numero valido.")
5else:
6 # Solo se ejecuta si no hubo error
7 print(f"El doble de tu numero es: {numero * 2}")
8 print(f"La mitad de tu numero es: {numero / 2}")
El bloque finally
El bloque finally se ejecuta siempre, haya o no error. Es perfecto para tareas de limpieza, como cerrar archivos o conexiones.
1try:
2 archivo = open("datos.txt", "r")
3 contenido = archivo.read()
4 print(contenido)
5except FileNotFoundError:
6 print("El archivo no existe.")
7finally:
8 # Esto se ejecuta SIEMPRE
9 print("Operacion de lectura finalizada.")
10
11# Ejemplo con archivo que se cierra siempre
12archivo = None
13try:
14 archivo = open("datos.txt", "r")
15 contenido = archivo.read()
16except FileNotFoundError:
17 print("Archivo no encontrado.")
18finally:
19 if archivo:
20 archivo.close()
21 print("Archivo cerrado correctamente.")
Estructura completa: try/except/else/finally
1try:
2 # Codigo que podria fallar
3 numero = int(input("Ingresa un numero: "))
4 resultado = 100 / numero
5except ValueError:
6 print("Error: ingresa un numero valido.")
7except ZeroDivisionError:
8 print("Error: no puedes dividir entre cero.")
9else:
10 # Solo si no hubo error
11 print(f"100 / {numero} = {resultado}")
12finally:
13 # Siempre se ejecuta
14 print("Gracias por usar la calculadora.")
try → except → else → finally. No puedes cambiar el orden. El else y finally son opcionales.
Lanzar excepciones con raise
Hasta ahora hemos atrapado errores que Python genera. Pero tambien puedes generar tus propios errores a proposito usando raise. Esto es util cuando quieres validar datos y detener la ejecucion si algo no es correcto.
1def establecer_edad(edad):
2 if edad < 0:
3 raise ValueError("La edad no puede ser negativa")
4 if edad > 150:
5 raise ValueError("La edad no puede ser mayor a 150")
6 return edad
7
8# Uso
9try:
10 mi_edad = establecer_edad(-5)
11except ValueError as e:
12 print(f"Error: {e}")
13 # Imprime: Error: La edad no puede ser negativa
¿Cuando usar raise?
Usa raise cuando tu funcion recibe datos que no tienen sentido para tu logica de negocio:
1def transferir_dinero(origen, destino, cantidad):
2 if cantidad <= 0:
3 raise ValueError("La cantidad debe ser positiva")
4 if cantidad > origen.saldo:
5 raise ValueError("Saldo insuficiente")
6
7 origen.saldo -= cantidad
8 destino.saldo += cantidad
9 return True
10
11def registrar_usuario(nombre, email):
12 if not nombre or not nombre.strip():
13 raise ValueError("El nombre no puede estar vacio")
14 if "@" not in email:
15 raise ValueError("El email no es valido")
16
17 # Continuar con el registro...
18 print(f"Usuario {nombre} registrado con {email}")
Crear excepciones personalizadas
Puedes crear tus propios tipos de excepciones creando una clase que herede de Exception. Esto es util en proyectos grandes para tener errores especificos de tu aplicacion.
1# Definir excepciones personalizadas
2class SaldoInsuficienteError(Exception):
3 """Se lanza cuando no hay suficiente saldo para la operacion."""
4 def __init__(self, saldo_actual, cantidad_solicitada):
5 self.saldo_actual = saldo_actual
6 self.cantidad_solicitada = cantidad_solicitada
7 mensaje = f"Saldo insuficiente. Tienes {saldo_actual}, pero necesitas {cantidad_solicitada}"
8 super().__init__(mensaje)
9
10class EdadInvalidaError(Exception):
11 """Se lanza cuando la edad no esta en un rango valido."""
12 pass
13
14class EmailInvalidoError(Exception):
15 """Se lanza cuando el formato del email es incorrecto."""
16 pass
Usando excepciones personalizadas
1class CuentaBancaria:
2 def __init__(self, titular, saldo=0):
3 self.titular = titular
4 self.saldo = saldo
5
6 def retirar(self, cantidad):
7 if cantidad <= 0:
8 raise ValueError("La cantidad debe ser positiva")
9 if cantidad > self.saldo:
10 raise SaldoInsuficienteError(self.saldo, cantidad)
11
12 self.saldo -= cantidad
13 return self.saldo
14
15# Uso
16cuenta = CuentaBancaria("Carlos", 1000)
17
18try:
19 cuenta.retirar(1500)
20except SaldoInsuficienteError as e:
21 print(f"No se pudo retirar: {e}")
22 print(f"Tu saldo actual: {e.saldo_actual}")
23 # Imprime: No se pudo retirar: Saldo insuficiente. Tienes $1000, pero necesitas $1500
24 # Tu saldo actual: $1000
Error (por convencion). Ejemplos: SaldoInsuficienteError, UsuarioNoEncontradoError, ConexionFallidaError.
Buenas practicas en el manejo de errores
Ahora que sabes como usar try/except, raise y excepciones personalizadas, veamos las reglas que siguen los programadores profesionales:
1. Nunca uses except generico sin tipo
1# MAL - atrapa TODO, incluso errores graves
2try:
3 resultado = hacer_algo()
4except:
5 pass # Silencia todos los errores
6
7# BIEN - atrapa solo lo que esperas
8try:
9 resultado = hacer_algo()
10except ValueError as e:
11 print(f"Valor invalido: {e}")
12except FileNotFoundError as e:
13 print(f"Archivo no encontrado: {e}")
2. Nunca uses pass en un except (silenciar errores)
1# MAL - el error desaparece y nunca sabras que fallo
2try:
3 dato = int(input("Numero: "))
4except ValueError:
5 pass
6
7# BIEN - al menos registra el error
8try:
9 dato = int(input("Numero: "))
10except ValueError:
11 print("Por favor ingresa un numero valido.")
12 dato = 0 # Valor por defecto
3. Pon solo lo necesario dentro del try
1# MAL - demasiado codigo en el try
2try:
3 nombre = input("Nombre: ")
4 edad = int(input("Edad: "))
5 email = input("Email: ")
6 ciudad = input("Ciudad: ")
7 print(f"Datos: {nombre}, {edad}, {email}, {ciudad}")
8except ValueError:
9 print("Error en la edad")
10
11# BIEN - solo la linea que puede fallar
12nombre = input("Nombre: ")
13try:
14 edad = int(input("Edad: "))
15except ValueError:
16 print("Error: la edad debe ser un numero")
17 edad = 0
18email = input("Email: ")
19ciudad = input("Ciudad: ")
4. Usa logging en lugar de print para errores en produccion
1import logging
2
3logging.basicConfig(level=logging.INFO)
4logger = logging.getLogger(__name__)
5
6try:
7 resultado = procesar_datos(datos)
8except ValueError as e:
9 logger.error(f"Error al procesar datos: {e}")
10except Exception as e:
11 logger.critical(f"Error inesperado: {e}", exc_info=True)
5. Usa with para manejo automatico de recursos
1# En lugar de try/finally para cerrar archivos, usa with
2with open("datos.txt", "r") as archivo:
3 contenido = archivo.read()
4# El archivo se cierra automaticamente, incluso si hay un error
Ejemplo practico: validador de datos del usuario
Vamos a crear un programa completo que pide datos al usuario y maneja todos los posibles errores de forma elegante:
1class ValidacionError(Exception):
2 """Error personalizado para validaciones."""
3 pass
4
5def pedir_nombre():
6 """Pide y valida el nombre del usuario."""
7 nombre = input("Ingresa tu nombre: ").strip()
8 if not nombre:
9 raise ValidacionError("El nombre no puede estar vacio")
10 if len(nombre) < 2:
11 raise ValidacionError("El nombre debe tener al menos 2 caracteres")
12 if any(char.isdigit() for char in nombre):
13 raise ValidacionError("El nombre no puede contener numeros")
14 return nombre
15
16def pedir_edad():
17 """Pide y valida la edad del usuario."""
18 entrada = input("Ingresa tu edad: ").strip()
19 try:
20 edad = int(entrada)
21 except ValueError:
22 raise ValidacionError(f"'{entrada}' no es un numero valido")
23
24 if edad < 0:
25 raise ValidacionError("La edad no puede ser negativa")
26 if edad > 120:
27 raise ValidacionError("La edad no parece valida (mayor a 120)")
28 return edad
29
30def pedir_email():
31 """Pide y valida el email del usuario."""
32 email = input("Ingresa tu email: ").strip()
33 if not email:
34 raise ValidacionError("El email no puede estar vacio")
35 if "@" not in email:
36 raise ValidacionError("El email debe contener @")
37 if "." not in email.split("@")[1]:
38 raise ValidacionError("El dominio del email debe contener un punto")
39 return email
40
41def pedir_numero_telefono():
42 """Pide y valida un numero de telefono."""
43 telefono = input("Ingresa tu telefono (10 digitos): ").strip()
44 if not telefono.isdigit():
45 raise ValidacionError("El telefono solo debe contener numeros")
46 if len(telefono) != 10:
47 raise ValidacionError(f"El telefono debe tener 10 digitos, tiene {len(telefono)}")
48 return telefono
49
50def pedir_dato_con_reintentos(funcion_pedir, max_intentos=3):
51 """Ejecuta una funcion de peticion con reintentos."""
52 for intento in range(1, max_intentos + 1):
53 try:
54 return funcion_pedir()
55 except ValidacionError as e:
56 print(f" Error: {e}")
57 if intento < max_intentos:
58 print(f" Intento {intento} de {max_intentos}. Intenta de nuevo.")
59 else:
60 print(f" Agotaste los {max_intentos} intentos.")
61 return None
62
63def main():
64 print("=" * 50)
65 print(" FORMULARIO DE REGISTRO")
66 print("=" * 50)
67 print()
68
69 nombre = pedir_dato_con_reintentos(pedir_nombre)
70 if not nombre:
71 print("No se pudo obtener el nombre. Saliendo...")
72 return
73
74 edad = pedir_dato_con_reintentos(pedir_edad)
75 if edad is None:
76 print("No se pudo obtener la edad. Saliendo...")
77 return
78
79 email = pedir_dato_con_reintentos(pedir_email)
80 if not email:
81 print("No se pudo obtener el email. Saliendo...")
82 return
83
84 telefono = pedir_dato_con_reintentos(pedir_numero_telefono)
85 if not telefono:
86 print("No se pudo obtener el telefono. Saliendo...")
87 return
88
89 # Todos los datos son validos
90 print()
91 print("=" * 50)
92 print(" REGISTRO EXITOSO")
93 print("=" * 50)
94 print(f" Nombre: {nombre}")
95 print(f" Edad: {edad} anios")
96 print(f" Email: {email}")
97 print(f" Telefono: {telefono}")
98
99if __name__ == "__main__":
100 main()
Resumen y proximo articulo
En este articulo aprendimos todo sobre el manejo de errores en Python:
- La diferencia entre errores de sintaxis y excepciones
- Como usar try/except para atrapar errores sin que el programa se detenga
- La importancia de atrapar excepciones especificas (ValueError, TypeError, etc.)
- Los bloques else (se ejecuta si no hay error) y finally (se ejecuta siempre)
- Como lanzar excepciones con
raisepara validar datos - Como crear excepciones personalizadas para tu aplicacion
- Las buenas practicas: no usar except generico, no silenciar errores, usar logging
- Un ejemplo completo de un programa robusto que valida datos del usuario
En el proximo y ultimo articulo (Parte 10 de 10) vamos a construir un proyecto final completo: un gestor de tareas en la terminal. Aplicaremos absolutamente todo lo que hemos aprendido en el curso: variables, funciones, listas, diccionarios, POO, archivos y manejo de errores.
¡Preparate para poner en practica todo lo aprendido!
Comments
Sign in to leave a comment
No comments yet. Be the first!