Conceptos basicos de try/except
Python usa excepciones para el manejo de errores en lugar de codigos de error. Cuando algo sale mal, Python lanza una excepcion. Capturas excepciones usando bloques try/except. Este enfoque lleva a un codigo mas limpio con una separacion clara entre el flujo normal y el manejo de errores.
# Basic try/except
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
# Catching multiple exceptions
try:
value = int("not a number")
except ValueError:
print("Invalid number format")
except TypeError:
print("Wrong type")
# Catching multiple exceptions in one clause
try:
# some operation
data = {"key": "value"}
result = data["missing"] + 1
except (KeyError, TypeError) as e:
print(f"Error: {e}")
# The full try/except/else/finally
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found!")
content = ""
else:
# Runs only if no exception occurred
print(f"Read {len(content)} characters")
finally:
# Always runs, even if exception occurred
print("Cleanup complete")
Jerarquia de excepciones
La jerarquia de excepciones de Python es un arbol de clases. Todas las excepciones heredan de BaseException, siendo Exception la base para la mayoria de los errores que capturaras. Entender la jerarquia te ayuda a capturar las excepciones correctas en el nivel de especificidad adecuado.
Tipos de excepciones comunes
- ValueError: Tipo correcto pero valor inapropiado (ej. int("abc"))
- TypeError: Operacion con tipo incorrecto (ej. "str" + 5)
- KeyError: Clave de diccionario no encontrada
- IndexError: Indice de secuencia fuera de rango
- AttributeError: El objeto no tiene ese atributo
- FileNotFoundError: El archivo o directorio no existe
- IOError/OSError: Error relacionado con el sistema (E/S de archivo, red, etc.)
- RuntimeError: Error generico de tiempo de ejecucion
Lanzamiento de excepciones
# Raise an exception
def validate_age(age):
if not isinstance(age, int):
raise TypeError(f"Age must be an integer, got {type(age).__name__}")
if age < 0 or age > 150:
raise ValueError(f"Age must be between 0 and 150, got {age}")
return age
# Re-raise an exception
try:
result = some_risky_operation()
except ValueError:
print("Logging the error...")
raise # Re-raises the caught exception
# Exception chaining
try:
data = load_config("config.json")
except FileNotFoundError as e:
raise RuntimeError("Failed to initialize application") from e
Excepciones personalizadas
Crear clases de excepcion personalizadas te permite definir errores especificos del dominio que hacen tu codigo mas legible y tu manejo de errores mas preciso. Siempre hereda de Exception (no de BaseException).
# Custom exception hierarchy
class AppError(Exception):
"""Base exception for our application."""
pass
class ValidationError(AppError):
"""Raised when input validation fails."""
def __init__(self, field: str, message: str):
self.field = field
self.message = message
super().__init__(f"Validation error on '{field}': {message}")
class NotFoundError(AppError):
"""Raised when a resource is not found."""
def __init__(self, resource: str, identifier):
self.resource = resource
self.identifier = identifier
super().__init__(f"{resource} with id '{identifier}' not found")
class AuthenticationError(AppError):
"""Raised when authentication fails."""
pass
# Using custom exceptions
def create_user(name: str, email: str):
if not name:
raise ValidationError("name", "Name cannot be empty")
if "@" not in email:
raise ValidationError("email", "Invalid email format")
return {"name": name, "email": email}
try:
user = create_user("", "invalid")
except ValidationError as e:
print(f"Field: {e.field}")
print(f"Message: {e.message}")
except AppError as e:
print(f"Application error: {e}")
Gestores de contexto
Los gestores de contexto garantizan la limpieza usando la sentencia with. Puedes crear los tuyos usando el protocolo __enter__/__exit__ o el decorador @contextmanager.
from contextlib import contextmanager
import time
# Class-based context manager
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.perf_counter() - self.start
print(f"Elapsed: {self.elapsed:.4f} seconds")
return False # Don't suppress exceptions
with Timer() as t:
total = sum(range(1_000_000))
print(f"Result: {total}")
# Generator-based context manager (simpler)
@contextmanager
def temporary_directory():
import tempfile
import shutil
tmpdir = tempfile.mkdtemp()
try:
yield tmpdir
finally:
shutil.rmtree(tmpdir)
with temporary_directory() as tmpdir:
print(f"Working in {tmpdir}")
# Directory is automatically cleaned up
Puntos clave
- Captura excepciones especificas: Nunca uses clausulas except: sin especificar
- Usa else y finally: else para logica de exito, finally para limpieza
- Crea excepciones personalizadas: Construye una jerarquia para los errores de tu aplicacion
- Gestores de contexto: Usa sentencias with para cualquier recurso que necesite limpieza