Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:programacion_desde_cero_con_python:errores_excepciones_y_depuracion

Errores, excepciones y depuración

Manejar lo inesperado y dejar rastro.

Este módulo forma parte del curso de programación desde cero con Python

Módulo 11. Errores, excepciones y depuración

En el Módulo 10 aprendiste a diseñar pruebas y a usar print() y breakpoint() para localizar fallos. Pero esos fallos, cuando ocurren en tiempo de ejecución, se manifiestan como excepciones: objetos que Python crea para señalar que algo ha ido mal. Hasta ahora, la mayoría de las veces has dejado que el programa se detenga con un traceback. En este módulo aprenderás a anticipar esos errores, capturarlos de forma selectiva, lanzar los tuyos propios y registrar lo que ocurre con logging, la herramienta profesional para diagnosticar problemas en producción.

El objetivo no es “evitar que el programa falle a toda costa”, sino que falle de forma controlada, informativa y recuperable cuando sea posible, y que deje un rastro claro cuando no lo sea.


1. Título y alcance

Título: Errores, excepciones y depuración: manejar lo inesperado y dejar rastro.

Alcance: Distinguir errores de sintaxis, de ejecución y de lógica; leer e interpretar tracebacks; conocer las excepciones incorporadas más frecuentes; usar try/except/else/finally con captura específica; lanzar excepciones con raise y encadenarlas con from; crear excepciones personalizadas; usar logging para registrar eventos sin depender de print(); depurar paso a paso con breakpoint() y pdb; y aplicar las buenas prácticas de PEP 8 sobre manejo de excepciones. Se presupone todo lo anterior, especialmente funciones (Módulo 9) y el proceso de pruebas (Módulo 10). No se introducen ExceptionGroup ni except* en profundidad (se mencionan como ampliación), ni concurrencia.


2. Objetivos de aprendizaje

Al terminar este módulo serás capaz de:

1. Diferenciar un SyntaxError (antes de ejecutar) de una excepción en tiempo de ejecución y de un error lógico. 2. Leer un traceback de arriba abajo e identificar el tipo de excepción, el mensaje y la línea exacta donde se originó. 3. Reconocer las excepciones incorporadas más comunes: ValueError, TypeError, KeyError, IndexError, ZeroDivisionError, FileNotFoundError. 4. Escribir bloques try/except que capturen solo las excepciones esperadas y en el mínimo código posible. 5. Usar else (se ejecuta si no hubo excepción) y finally (se ejecuta siempre) para organizar la lógica y la limpieza. 6. Lanzar excepciones con raise y encadenarlas con raise ... from ... para preservar la causa original. 7. Definir excepciones personalizadas heredando de Exception. 8. Configurar logging para registrar mensajes con niveles (DEBUG, INFO, WARNING, ERROR, CRITICAL) y evitar print() en código de producción. 9. Usar breakpoint() y los comandos básicos de pdb (p, n, s, c, q, l) para inspeccionar el estado del programa. 10. Aplicar las recomendaciones de PEP 8: no usar except: desnudo, no silenciar excepciones, limitar el try a lo imprescindible.


3. Conocimientos previos

- Del Módulo 6: Condiciones y truthiness. - Del Módulo 7: Bucles y control de flujo. - Del Módulo 8: Colecciones (listas, diccionarios), acceso por índice y por clave. - Del Módulo 9: Funciones, return, ámbito y docstrings. - Del Módulo 10: Especificación de problemas, pruebas con unittest, print() y breakpoint(). - Ningún conocimiento de try/except, raise ni logging.


4. Introducción motivadora: el programa que se rompe sin avisar

Un programa que pide un número y el usuario escribe "hola" no puede continuar: int("hola") no tiene sentido. Hasta ahora, ese programa se detenía con un ValueError y un traceback de varias líneas. En un entorno de aprendizaje, eso es aceptable; en una aplicación real, es inaceptable. El usuario no debería ver un volcado de pila, y el programa debería poder recuperarse (pedir el dato de nuevo) o, al menos, registrar el problema y terminar con un mensaje claro.

Las excepciones son el mecanismo que Python ofrece para separar el flujo normal del flujo de error. Con try/except puedes decidir qué hacer cuando algo falla; con logging puedes dejar constancia de lo ocurrido sin interrumpir al usuario. Y con raise puedes señalar errores en tu propio código cuando una condición no se cumple. Este módulo te da el control sobre lo que antes solo mirabas desde fuera.


5. Explicación paso a paso

5.1. Tres tipos de errores

No todos los errores son iguales. Distinguirlos es el primer paso para diagnosticarlos.

TipoCuándo ocurreEjemplo¿Se puede capturar con try?
Sintaxis (SyntaxError)Antes de ejecutar, al analizar el código.print("Hola"No: el programa ni siquiera empieza.
Ejecución (excepción)Durante la ejecución, en una operación concreta.int("hola") → ValueErrorSí, con try/except.
LógicoEl programa funciona, pero el resultado es incorrecto.edad > 18 cuando debería ser >= 18No se captura: hay que revisar el diseño y las pruebas.
[ERROR HABITUAL] Confundir un error de ejecución con uno de sintaxis. Un SyntaxError impide ejecutar cualquier línea del programa, incluso las anteriores al error. Una excepción en tiempo de ejecución solo interrumpe el flujo cuando se alcanza la instrucción problemática.

5.2. Anatomía de un *traceback*

Cuando una excepción no se captura, Python imprime un traceback: la secuencia de llamadas que llevaron al error. Leerlo correctamente ahorra minutos de desconcierto.

Traceback (most recent call last):
  File "ejemplo.py", line 5, in <module>
    resultado = dividir(10, 0)
  File "ejemplo.py", line 2, in dividir
    return a / b
ZeroDivisionError: division by zero

Cómo leerlo, de abajo arriba:

1. La última línea indica el tipo de excepción y el mensaje: ZeroDivisionError: division by zero. 2. La línea inmediatamente anterior muestra el código exacto que falló: return a / b. 3. Las líneas anteriores muestran la cadena de llamadas: quién llamó a quién. La primera entrada (<module>) es el punto de entrada del programa; la última es donde ocurrió el error.

[PYTHON] En Python 3.15, los tracebacks son más informativos: pueden incluir sugerencias (“Did you mean…?”) cuando el error se debe a un nombre mal escrito o a un método de otro lenguaje, y resaltado en color en la terminal. Por ejemplo, si escribes [1, 2, 3].push(4), el mensaje sugiere append, que es el método correcto de las listas en Python. Esta mejora no cambia la lógica del manejo de excepciones, pero facilita enormemente la depuración.
[BUENA PRÁCTICA] No te quedes con la primera línea del traceback. La información más útil está al final: el tipo de excepción y el mensaje. Y si el error ocurre dentro de una función, la línea que lo originó es la última de la pila, no la primera.

5.3. Excepciones incorporadas más frecuentes

ExcepciónCuándo ocurreEjemplo
ValueErrorEl tipo es correcto, pero el valor no es válido.int("hola")
TypeErrorEl tipo no es el adecuado para la operación."2" + 2
KeyErrorSe accede a una clave inexistente en un diccionario.{"a": 1}["b"]
IndexErrorSe accede a un índice fuera de rango en una secuencia.[1, 2][5]
ZeroDivisionErrorDivisión entre cero.10 / 0
FileNotFoundErrorSe intenta abrir un archivo que no existe.open("no_existe.txt")
AttributeErrorSe accede a un atributo o método que no existe."texto".metodo_falso()
NameErrorSe usa un nombre no definido.print(variable_inexistente)
[PYTHON] La jerarquía de excepciones importa: todas heredan de BaseException, pero las que deberías capturar normalmente heredan de Exception. KeyboardInterrupt y SystemExit heredan directamente de BaseException para que un except Exception no las capture por accidente. Esta distinción es deliberada y se explica en el apartado 5.7.

5.4. `try`/`except`: capturar lo esperado

La estructura básica es:

try:
    # código que puede fallar
except TipoDeExcepcion:
    # qué hacer si ocurre esa excepción

Ejemplo:

try:
    edad = int(input("Introduce tu edad: "))
    print(f"Tienes {edad} años.")
except ValueError:
    print("Eso no es un número entero válido.")

Seguimiento: Si el usuario escribe 25, no hay excepción y se ejecuta el try completo. Si escribe "veinticinco", int() lanza ValueError, el flujo salta al except y se muestra el mensaje. El programa no se detiene.

Puedes capturar varios tipos de excepción a la vez:

try:
    resultado = 10 / int(input("Divisor: "))
except (ValueError, ZeroDivisionError) as error:
    print(f"No se pudo calcular: {error}")

La variable error (o e) contiene la instancia de la excepción, con su mensaje y otros atributos.

[ERROR HABITUAL] Usar except: sin especificar el tipo. Esto captura todas las excepciones, incluidas KeyboardInterrupt y SystemExit, y oculta errores de programación que deberían propagarse. PEP 8 lo desaconseja explícitamente. Captura siempre el tipo concreto que esperas.
[BUENA PRÁCTICA] Limita el try al mínimo código posible. Si el try abarca veinte líneas, una excepción en cualquiera de ellas activará el except, y será difícil saber qué falló. Coloca dentro del try solo la operación que puede lanzar la excepción que quieres manejar.

5.5. `else` y `finally`

Dos cláusulas opcionales completan la estructura:

- else: se ejecuta solo si no hubo excepción en el try. - finally: se ejecuta siempre, haya habido excepción o no. Se usa para limpieza (cerrar archivos, liberar recursos).

try:
    numero = int(input("Número: "))
except ValueError:
    print("Entrada no válida.")
else:
    print(f"El cuadrado es {numero ** 2}.")
finally:
    print("Fin del intento.")

Seguimiento: Si la conversión falla, se ejecuta except y luego finally. Si la conversión tiene éxito, se ejecuta else y luego finally.

[PYTHON] finally es la forma de garantizar la limpieza incluso cuando se produce un return, un break o una excepción no capturada. En el Módulo 13 verás que los gestores de contexto (with) son una alternativa más idiomática para recursos como archivos, pero finally sigue siendo la herramienta general.

5.6. `raise`: lanzar excepciones

A veces tu código detecta una situación inválida y necesita señalarla. Para eso existe raise:

def calcular_raiz_cuadrada(x):
    """Devuelve la raíz cuadrada de x. Lanza ValueError si x es negativo."""
    if x < 0:
        raise ValueError("No se puede calcular la raíz cuadrada de un número negativo.")
    return x ** 0.5

raise interrumpe el flujo normal y propaga la excepción hacia arriba hasta que alguien la capture o el programa termine.

Encadenar excepciones con from: cuando una excepción se produce como consecuencia de otra, puedes preservar la causa original:

try:
    valor = int("hola")
except ValueError as error:
    raise RuntimeError("No se pudo procesar la configuración") from error

El traceback mostrará ambas excepciones, dejando claro que RuntimeError fue causado por ValueError.

[PYTHON] Encadenar excepciones con from es una práctica recomendada para no perder información de diagnóstico. Sin from, Python también muestra la excepción original, pero con from la relación causal es explícita.

Excepciones personalizadas: puedes definir tus propias excepciones heredando de Exception:

class SaldoInsuficienteError(Exception):
    """Se lanza cuando una cuenta no tiene saldo suficiente."""
    pass
 
def retirar(saldo, cantidad):
    if cantidad > saldo:
        raise SaldoInsuficienteError(f"Saldo {saldo} insuficiente para retirar {cantidad}.")
    return saldo - cantidad

Las excepciones personalizadas comunican la intención del dominio del problema y permiten capturarlas de forma específica.

5.7. Captura específica y jerarquía de excepciones

Capturar la excepción correcta es esencial. Un except Exception captura casi todo, pero puede ocultar errores que no esperabas. Un except desnudo captura incluso KeyboardInterrupt, lo que impide interrumpir el programa con Ctrl + C.

Orden de los except: si capturas varias excepciones, coloca las más específicas primero:

try:
    # ...
except ZeroDivisionError:
    # ...
except ArithmeticError:
    # ...
except Exception:
    # ...

Como ZeroDivisionError hereda de ArithmeticError y esta de Exception, el orden importa: si Exception fuera primero, capturaría todo y los except posteriores nunca se ejecutarían.

[PYTHON] Python 3.15 estabiliza la sintaxis except* para manejar ExceptionGroup (grupos de excepciones), introducida en Python 3.11. Esto es relevante en código concurrente (por ejemplo, con asyncio.TaskGroup), donde varias tareas pueden fallar a la vez. No lo necesitas para el código secuencial de este curso, pero conviene que sepas que existe si lees código moderno.

5.8. `logging`: registrar en lugar de imprimir

print() es útil para depurar, pero no para producción: no distingue niveles, no incluye marcas de tiempo y no se puede desactivar fácilmente. El módulo logging de la biblioteca estándar resuelve esos problemas.

import logging
 
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)
 
logging.debug("Esto es un detalle de depuración.")
logging.info("El programa ha arrancado.")
logging.warning("Queda poco espacio en disco.")
logging.error("No se pudo conectar con la base de datos.")
logging.critical("Fallo catastrófico.")

Niveles (de menor a mayor gravedad): DEBUG, INFO, WARNING, ERROR, CRITICAL. basicConfig(level=...) establece el nivel mínimo que se mostrará. Si el nivel es INFO, los mensajes DEBUG se ignoran.

[PYTHON] En Python 3.15, el rendimiento de logging ha mejorado significativamente, especialmente en entornos multihebra, y se ha añadido soporte para configuraciones más flexibles a través de variables de entorno. Además, structlog (una biblioteca de logging estructurado) aprovecha las importaciones perezosas (lazy imports, PEP 810) de Python 3.15 para que import structlog sea más rápido, cargando los módulos de logging solo cuando se usan. Para este curso, logging de la biblioteca estándar es más que suficiente.
[BUENA PRÁCTICA] Usa logging en lugar de print() para cualquier mensaje que no sea una salida deliberada para el usuario. print() es para la interfaz de usuario; logging es para el desarrollador y para el diagnóstico. Configura el nivel adecuado según el entorno: DEBUG en desarrollo, INFO o WARNING en producción.

5.9. Depuración paso a paso con `breakpoint()` y `pdb`

En el Módulo 10 introdujiste breakpoint() para inspeccionar variables. Ahora que conoces las excepciones, puedes combinarlo con try/except para depurar el estado justo antes de que algo falle:

try:
    resultado = operacion_compleja(datos)
except Exception:
    breakpoint()  # Inspecciona el estado antes de decidir qué hacer
    raise

Dentro de pdb, además de p, n, s, c, q y l (vistos en el Módulo 10), puedes usar:

- w (where): muestra la pila de llamadas actual. - u (up) y d (down): se mueven entre los marcos de la pila. - bt (backtrace): alias de w. - pp variable: imprime con formato.

[PYTHON] pdb es el depurador estándar, pero existen alternativas como pdb++ o los depuradores integrados de los IDE. Todos ellos se conectan al mismo mecanismo subyacente. breakpoint() es portable y no requiere instalar nada.

6. Ejemplos comentados

Ejemplo 1: Validación de entrada con try/except.

def pedir_entero(mensaje):
    """Pide un entero al usuario, repitiendo hasta que sea válido."""
    while True:
        try:
            return int(input(mensaje))
        except ValueError:
            print("Entrada no válida. Inténtalo de nuevo.")

Seguimiento: El bucle while True se repite hasta que int() tiene éxito y se ejecuta return. Si el usuario escribe algo no numérico, ValueError se captura y se muestra un mensaje, sin salir del bucle. Este patrón es muy común en interfaces de terminal.

Ejemplo 2: else y finally con un archivo (simulado).

import logging
 
logging.basicConfig(level=logging.INFO)
 
def procesar_archivo(ruta):
    try:
        with open(ruta, encoding="utf-8") as f:
            contenido = f.read()
    except FileNotFoundError:
        logging.error("El archivo %s no existe.", ruta)
        return None
    else:
        logging.info("Archivo leído correctamente: %d caracteres.", len(contenido))
        return contenido
    finally:
        logging.debug("Intento de lectura finalizado para %s.", ruta)

Seguimiento: else solo se ejecuta si open() y read() no lanzan excepción. finally se ejecuta siempre, tanto si el archivo existe como si no. El with (Módulo 13) garantiza el cierre del archivo.

Ejemplo 3: Excepción personalizada y encadenamiento.

class ConfiguracionInvalidaError(Exception):
    """La configuración no cumple el formato esperado."""
    pass
 
def cargar_configuracion(texto):
    try:
        partes = texto.split("=")
        clave, valor = partes
    except ValueError as error:
        raise ConfiguracionInvalidaError(f"Formato inválido: {texto!r}") from error
    return {clave.strip(): valor.strip()}

Seguimiento: Si texto no contiene exactamente un =, partes tendrá más o menos de dos elementos y el desempaquetado lanzará ValueError. Se captura y se relanza como ConfiguracionInvalidaError, preservando la causa original con from error.


7. Seguimiento de la ejecución

Cuando se lanza una excepción dentro de un try, Python busca el primer except compatible en el mismo try. Si lo encuentra, ejecuta su bloque y no propaga la excepción. Si no lo encuentra, la excepción se propaga al llamador (la función que llamó a la actual). Si nadie la captura, el programa termina y se imprime el traceback.

La cláusula else se ejecuta solo si el try termina sin excepción. La cláusula finally se ejecuta siempre, antes de que la excepción se propague (si no fue capturada) o después del except/else (si fue capturada).


8. Errores habituales

ErrorCausaCómo evitarlo
except: desnudoCaptura todo, incluidos KeyboardInterrupt y SystemExit.Captura el tipo específico (except ValueError:).
Silenciar la excepciónexcept ValueError: pass sin registrar nada.Al menos registra con logging o muestra un mensaje.
try demasiado amplioEl try abarca código que no puede fallar de la forma esperada.Reduce el try a la operación concreta.
No usar finally para limpiezaUn recurso queda abierto si hay excepción.Usa finally o un gestor de contexto (with).
raise sin mensaje útilraise ValueError() sin explicar qué pasó.Incluye un mensaje descriptivo con el valor problemático.
Capturar y no relanzarexcept Exception: return None oculta el error.Si no puedes recuperarte, relanza (raise) o registra y relanza.
Usar print() en producciónNo hay niveles, ni marcas de tiempo, ni control.Usa logging.
[ERROR HABITUAL] Un except que captura Exception y luego hace pass es una de las peores prácticas: el programa continúa como si nada, pero el error queda oculto. Si no sabes qué hacer con una excepción, no la captures. Deja que se propague y se manifieste.

9. Particularidades de Python frente a otros lenguajes

- Excepciones para control de flujo: en Python, es idiomático usar excepciones para casos excepcionales, pero no para el flujo normal. Por ejemplo, StopIteration se usa internamente para terminar un for, pero no deberías lanzarla manualmente en tu lógica diaria. - else en try: Python es uno de los pocos lenguajes que ofrece una cláusula else para try, que se ejecuta solo si no hubo excepción. En Java o C#, esto se simula colocando el código después del try/catch, pero sin la garantía de que solo se ejecute en caso de éxito. - finally siempre se ejecuta: incluso si hay un return dentro del try, el bloque finally se ejecuta antes de que la función devuelva el valor. Esto es útil para limpieza, pero puede sorprender si no se espera. - Excepciones como objetos: en Python, las excepciones son objetos que puedes inspeccionar, encadenar y personalizar. En C, el manejo de errores suele basarse en códigos de retorno; en Java, las excepciones son objetos, pero la jerarquía y el encadenamiento tienen diferencias.


10. Buenas prácticas

Incorrecto (captura demasiado amplia y silenciosa):

try:
    hacer_algo()
except:
    pass

Mejor (captura específica, registro y relanzamiento si no se puede recuperar):

import logging
 
try:
    hacer_algo()
except ValueError as error:
    logging.error("Valor inválido: %s", error)
    raise  # Relanza para que el llamador decida
[BUENA PRÁCTICA] Sigue las recomendaciones de PEP 8: captura excepciones específicas, limita el try al mínimo código necesario y no uses except: desnudo. Si no puedes manejar una excepción de forma significativa, no la captures: deja que se propague.

11. Windows y Linux

- logging funciona igual en ambos sistemas. La salida por defecto es stderr, que está disponible en ambas terminales. - Los tracebacks son idénticos en estructura. Las rutas de archivo difieren (\ en Windows, / en Linux), pero Python las muestra correctamente en cada sistema. - breakpoint() y pdb son portables. Los comandos son los mismos. - Archivos y codificación: al abrir archivos, especifica encoding="utf-8" explícitamente si el archivo no está en la codificación predeterminada del sistema. En Python 3.15, UTF-8 es el predeterminado, pero ser explícito evita sorpresas en ambos sistemas.

[WINDOWS / LINUX] Si registras mensajes con logging en un archivo, usa pathlib (Módulo 13) para construir la ruta de forma portable. Evita concatenar cadenas con \ o / codificadas a mano.

12. Ejercicios guiados

Ejercicio guiado 1: Leer un traceback.

Ejecuta este código y lee el traceback. Identifica el tipo de excepción, el mensaje y la línea exacta donde se originó.

def dividir(a, b):
    return a / b
 
print(dividir(10, 0))

Ejercicio guiado 2: try/except con validación.

Escribe una función pedir_edad() que pida la edad al usuario y devuelva un entero. Si la entrada no es un número, debe mostrar un mensaje y volver a pedirla (bucle while True con try/except ValueError).

Ejercicio guiado 3: else y finally.

Escribe una función leer_numero_de_archivo(ruta) que intente leer un número de un archivo. Usa try/except FileNotFoundError/else/finally. En else, muestra el número; en finally, registra con logging que el intento ha terminado.


13. Ejercicios autónomos

1. Predicción: ¿Qué muestra este código? Razona antes de ejecutar.

  ```python
  try:
      print(1 / 0)
  except ZeroDivisionError:
      print("División por cero")
  finally:
      print("Siempre")
  ```

2. Excepción personalizada: Define NotaInvalidaError y una función validar_nota(nota) que lance la excepción si nota no está entre 0 y 10. Prueba con 5, -1 y 11.

3. logging: Configura logging para que muestre mensajes de nivel INFO o superior con el formato "%(levelname)s: %(message)s". Escribe una función que registre un warning si una lista está vacía y un info con la longitud en caso contrario.

4. Encadenamiento: Escribe una función cargar_entero(texto) que intente int(texto) y, si falla, lance ValueError("El texto no es un entero") encadenado desde la excepción original con from.

5. Reto opcional: Investiga qué es ExceptionGroup y la sintaxis except* (Python 3.11+, estabilizada en 3.15). Escribe un ejemplo mínimo que lance un ExceptionGroup con dos excepciones y capture solo una de ellas con except*.


14. Soluciones razonadas

Solución al ejercicio guiado 1: El traceback muestra ZeroDivisionError: division by zero en la línea return a / b de la función dividir, llamada desde la línea print(dividir(10, 0)). La causa es la división entre cero.

Solución al ejercicio guiado 2:

def pedir_edad():
    while True:
        try:
            return int(input("Introduce tu edad: "))
        except ValueError:
            print("Eso no es un número entero. Inténtalo de nuevo.")

El return dentro del try hace que la función termine en cuanto la conversión tiene éxito. Si falla, el except muestra el mensaje y el bucle continúa.

Solución al ejercicio guiado 3:

import logging
 
logging.basicConfig(level=logging.INFO)
 
def leer_numero_de_archivo(ruta):
    try:
        with open(ruta, encoding="utf-8") as f:
            contenido = f.read()
    except FileNotFoundError:
        logging.error("Archivo no encontrado: %s", ruta)
        return None
    else:
        numero = float(contenido)
        print(f"Número leído: {numero}")
        return numero
    finally:
        logging.info("Intento de lectura finalizado.")

Solución al ejercicio 1 (predicción): Muestra "División por cero" y luego "Siempre". La excepción se captura en el except; finally se ejecuta después, siempre.

Solución al ejercicio 2 (excepción personalizada):

class NotaInvalidaError(Exception):
    """La nota no está en el rango permitido (0-10)."""
    pass
 
def validar_nota(nota):
    if not 0 <= nota <= 10:
        raise NotaInvalidaError(f"Nota fuera de rango: {nota}")
    return nota
 
for n in (5, -1, 11):
    try:
        print(validar_nota(n))
    except NotaInvalidaError as error:
        print(f"Error: {error}")

Solución al ejercicio 4 (encadenamiento):

def cargar_entero(texto):
    try:
        return int(texto)
    except ValueError as error:
        raise ValueError(f"El texto {texto!r} no es un entero") from error

El from error preserva la causa original en el traceback.


15. Resumen

- Los errores pueden ser de sintaxis (antes de ejecutar), de ejecución (excepciones) o lógicos (resultado incorrecto sin excepción). - Un traceback se lee de abajo arriba: la última línea indica el tipo y el mensaje; las anteriores, la cadena de llamadas. - try/except captura excepciones específicas; else se ejecuta si no hubo excepción; finally se ejecuta siempre. - raise lanza una excepción; raise ... from ... encadena la causa original. - Las excepciones personalizadas heredan de Exception y comunican errores del dominio del problema. - logging sustituye a print() en código de producción: niveles, marcas de tiempo y control de la salida. - breakpoint() y pdb permiten inspeccionar el estado paso a paso. - PEP 8 recomienda capturar excepciones específicas, limitar el try al mínimo y no usar except: desnudo. - Python 3.15 mejora los mensajes de error con sugerencias y color, y estabiliza except* para grupos de excepciones (útil en concurrencia).


16. Autoevaluación

1. ¿Cuál es la diferencia entre un SyntaxError y una excepción en tiempo de ejecución? 2. ¿Cómo se lee un traceback? ¿Dónde está la información más importante? 3. ¿Qué hace finally y en qué se diferencia de else? 4. ¿Por qué no se recomienda except: desnudo? ¿Qué alternativa es correcta? 5. Reto: Sin ejecutar, predice qué muestra este código y explica el flujo:

  ```python
  try:
      x = int("abc")
  except ValueError:
      print("A")
  else:
      print("B")
  finally:
      print("C")
  ```

informatica/programacion/cursos/programacion_desde_cero_con_python/errores_excepciones_y_depuracion.txt · Última modificación: por tempwin