2.4.-Depurar programas
2.4. Depuración de programas¶
Idea principal
Depurar es investigar el comportamiento de un programa para localizar la causa de un problema y corregirlo. El depurador permite detener la ejecución, avanzar paso a paso e inspeccionar las variables y la pila de llamadas.
Un programa puede terminar con una excepción, producir una salida incorrecta o quedarse ejecutándose indefinidamente. En los tres casos necesitamos obtener evidencias sobre lo que está ocurriendo, en lugar de cambiar instrucciones al azar. La depuración (debugging) proporciona herramientas para observar el estado del programa mientras se ejecuta.
La depuración no consiste solo en poner print() por todas partes. Los mensajes
temporales pueden ayudar en un caso sencillo, pero un depurador permite detenerse
en un punto concreto, revisar el valor de una expresión y seguir la ejecución
sin modificar el código. También conviene combinarlo con pruebas automatizadas,
aserciones y registros (logging) según el entorno en el que se ejecute la
aplicación.
El objetivo de este tema no es memorizar botones del entorno, sino entender el flujo de ejecución, formular hipótesis a partir de evidencias y saber comprobar que una corrección no ha introducido otro problema.
La normativa del módulo de Programación relaciona este tema con el siguiente resultado de aprendizaje y sus criterios de evaluación:
| Código | Descripción literal de la normativa |
|---|---|
| RA 3 | Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje. |
| CE 3.f | Se han probado y depurado los programas. |
Se trabajan los criterios que tienen que ver con la depuración de programas, la localización de errores y la verificación de correcciones.
Qué deberías saber al terminar
Al acabar este tema deberías poder:
- distinguir errores de sintaxis, de ejecución y de lógica;
- reproducir un problema y formular una hipótesis comprobable;
- establecer puntos de interrupción y ejecutar un programa paso a paso;
- inspeccionar variables, expresiones, la consola y la pila de llamadas;
- utilizar el depurador de Visual Studio Code y reconocer
pdb; - corregir un programa y verificarlo con casos normales y casos límite.
Mapa del tema
En este documento seguiremos esta progresión:
- qué significa depurar y qué tipos de problemas existen;
- proceso sistemático y herramientas de depuración;
- depuración paso a paso con Visual Studio Code;
- corrección y análisis de un programa Python;
- buenas prácticas, errores frecuentes y actividades.
1. Qué significa depurar¶
Cuando el resultado no coincide con lo esperado, la primera tarea es describir el comportamiento observable: qué datos se introdujeron, qué salida apareció, qué excepción se produjo o en qué punto dejó de avanzar. Después se busca la causa, se aplica una corrección pequeña y se vuelve a ejecutar el caso que fallaba.
Depuración
La depuración es el proceso sistemático de localizar, comprender y corregir defectos de un programa mediante la observación de su ejecución y de su estado interno.
La depuración puede ser necesaria aunque el programa no muestre ningún mensaje de error. Por ejemplo, una función puede devolver el máximo y el mínimo sin lanzar una excepción, pero utilizar datos fuera del intervalo que especificaba el enunciado. Python ejecuta el código, aunque el resultado sea incorrecto.
1.1. Tipos de problemas¶
| Problema | Cuándo aparece | Ejemplo |
|---|---|---|
| Error de sintaxis | Python no puede interpretar el código. | range(0..10) o falta de :. |
| Error de ejecución | Una operación no puede completarse durante la ejecución. | Convertir "hola" a int. |
| Error de lógica | El programa se ejecuta, pero decide o calcula mal. | Usar or cuando se necesita and. |
| Error de configuración | El entorno no tiene el intérprete o dependencia esperada. | Seleccionar otro intérprete de Python. |
Los errores de sintaxis deben corregirse antes de iniciar una sesión normal de depuración: si Python no puede analizar el archivo, no puede ejecutar sus instrucciones. Los errores de ejecución suelen aparecer como excepciones y los errores de lógica requieren comparar el estado y la salida con el resultado esperado.
Un programa que funciona, pero no cumple la especificación
Si el enunciado pide diez números aleatorios entre 1 y 100 y el programa
utiliza random.randint(1, 1000), el programa se ejecuta correctamente, pero
puede generar datos fuera del intervalo permitido. El depurador permite
observar los valores reales y relacionarlos con la decisión incorrecta.
1.2. Un proceso de depuración reproducible¶
Depurar es más eficaz cuando se sigue un orden. El siguiente flujo evita confundir una hipótesis con un hecho:
flowchart TD
A["Reproducir el problema<br/>con datos conocidos"] --> B["Observar la salida,<br/>la excepción o el bloqueo"]
B --> C["Formular una hipótesis<br/>sobre la causa"]
C --> D["Elegir un punto de interrupción<br/>y observar el estado"]
D --> E["Ejecutar paso a paso<br/>e inspeccionar valores"]
E --> F{"¿El estado es el esperado?"}
F -->|No| G["Aislar la causa<br/>y corregirla"]
G --> H["Repetir el caso<br/>que fallaba"]
H --> E
F -->|Sí| I["Probar otros casos<br/>y documentar la solución"]
Un caso reproducible es especialmente importante cuando intervienen entradas aleatorias, fechas, red o ficheros. Durante la investigación se puede fijar una semilla aleatoria, utilizar datos de prueba conocidos o guardar la entrada que provocó el fallo. La intención es que otra persona pueda repetir la observación.
2. Herramientas y conceptos del depurador¶
La herramienta adecuada depende del problema y del entorno. Estas opciones no son excluyentes:
| Herramienta | Para qué sirve | Ejemplo |
|---|---|---|
print() |
Observar rápidamente un valor intermedio. | print("total:", total) |
| Depurador del IDE | Detener, avanzar e inspeccionar el estado. | F5, F10 y F11 en VS Code. |
pdb |
Depurar desde la terminal con la biblioteca estándar. | python -m pdb programa.py |
logging |
Registrar información con niveles y destinos configurables. | logger.info("Pedido procesado") |
assert |
Comprobar invariantes durante el desarrollo. | assert total >= 0 |
| Pruebas | Verificar automáticamente resultados esperados. | pytest y casos límite. |
Una aserción expresa una condición que el programa considera verdadera durante
el desarrollo. Si la condición es falsa, Python lanza AssertionError y el
depurador permite investigar el estado que la ha provocado:
def calcular_media(valores: list[float]) -> float:
assert valores, "La lista no puede estar vacía durante el desarrollo."
return sum(valores) / len(valores)
Las aserciones sirven para detectar invariantes y errores de programación, no
para validar datos externos: Python puede desactivarlas con python -O. Para
una entrada de una persona usuaria o de un servicio se debe utilizar una
condición explícita y tratar el resultado con el mecanismo adecuado.
Depurar no es lo mismo que controlar una excepción
Un bloque try-except decide cómo responder a una excepción prevista; el
depurador ayuda a investigar por qué se llegó a ese estado. Capturar una
excepción sin comprenderla puede ocultar un error lógico. Consulta también
control de excepciones.
2.1. pdb: el depurador de la biblioteca estándar¶
Python incluye el módulo pdb, que permite investigar un programa desde la
terminal. También se puede introducir breakpoint() en una línea concreta para
iniciar el depurador cuando la ejecución llegue a ella:
def dividir(total: float, cantidad: int) -> float:
"""Divide un total entre una cantidad positiva."""
breakpoint()
return total / cantidad
resultado = dividir(10, 2)
print(resultado)
Otra posibilidad es iniciar el programa directamente con pdb:
Durante una sesión de pdb, algunos comandos habituales son:
| Comando | Acción |
|---|---|
n |
Ejecuta la siguiente línea sin entrar en una función. |
s |
Ejecuta la siguiente línea entrando en una función propia. |
c |
Continúa hasta el siguiente punto de interrupción. |
p expresión |
Muestra el valor de una expresión. |
w |
Muestra la pila de llamadas. |
q |
Termina la sesión de depuración. |
La función incorporada breakpoint() es cómoda durante el desarrollo, pero no
debe quedar olvidada en una entrega o en producción. Antes de usarla, conviene
comprobar que el proyecto no desactiva los puntos de interrupción mediante la
configuración de PYTHONBREAKPOINT.
3. Depurar con Visual Studio Code¶
Visual Studio Code puede depurar programas Python si está instalada la extensión Python de Microsoft y el editor utiliza el intérprete correcto. El nombre habitual del editor es Visual Studio Code, aunque en apuntes antiguos puede aparecer como «Visual Code».
3.1. Preparar el entorno¶
Antes de iniciar el depurador:
- Instala la extensión Python desde Extensions.
- Abre la carpeta del proyecto, no solo el archivo suelto si el proyecto tiene configuración propia.
- Selecciona el intérprete con Python: Select Interpreter.
- Abre el archivo
.pyque quieres ejecutar. - Comprueba primero que el programa se puede ejecutar de forma normal.
Si el proyecto necesita argumentos, variables de entorno o un directorio de
trabajo concreto, puedes configurar un perfil en .vscode/launch.json. Para
los primeros ejemplos, el perfil que ofrece Run and Debug suele ser
suficiente.
3.2. Puntos de interrupción y estado del programa¶
Un breakpoint, punto de interrupción o punto de parada detiene el flujo de ejecución justo antes de ejecutar la instrucción marcada. Se puede colocar haciendo clic en el margen izquierdo de una línea o con F9. El programa debe recorrer esa línea para que el punto se active; colocar un punto dentro de una rama que nunca se cumple no detendrá la ejecución.
Para comenzar, selecciona Run and Debug en la barra lateral y pulsa Start Debugging o F5. La línea resaltada indica la próxima instrucción que se ejecutará. En el panel Variables se muestran, entre otros datos, las variables locales del marco actual y las variables globales disponibles.
3.3. Ejecutar paso a paso¶
Las órdenes principales del depurador son:
| Orden | Atajo habitual | Comportamiento |
|---|---|---|
| Continue | F5 | Continúa hasta el siguiente breakpoint o hasta terminar. |
| Step Over | F10 | Ejecuta la línea sin entrar en la función que llama. |
| Step Into | F11 | Ejecuta la línea y entra en una función propia si procede. |
| Step Out | Mayús.+F11 | Termina la función actual y vuelve a quien la llamó. |
| Stop | Mayús.+F5 | Detiene la sesión de depuración. |
Step Into permite seguir una función línea a línea. Si solo quieres utilizar una función que ya has comprobado, Step Over la ejecuta completa y evita entrar en su implementación. Si has entrado demasiado, Step Out vuelve al marco que hizo la llamada.
Al pulsar Step Into o F11 en el ejemplo, se ejecuta la siguiente
instrucción. Si acabamos de crear numeros = [], el panel Variables mostrará
una lista vacía. Después, al recorrer el bucle, podremos ver cómo se añaden
elementos.
3.4. Watch, Debug Console y Call Stack¶
El panel Watch permite añadir expresiones que queremos observar durante la
sesión. Por ejemplo, len(numeros) muestra cómo cambia el tamaño de la lista y
numeros muestra sus elementos. Las expresiones se evalúan con el contexto del
marco seleccionado; una variable local puede no estar disponible después de
salir de su función.
len(numeros).La Debug Console se abre normalmente en la parte inferior. Durante una parada podemos consultar expresiones Python usando los valores actuales del programa:
Estas expresiones ayudan a comprobar una hipótesis sin añadir un print() al
código. Hay que recordar que una consola de depuración puede permitir también
modificar el estado; para una investigación reproducible, anota cualquier
modificación y vuelve a ejecutar desde el principio antes de validar el arreglo.
El panel Call Stack muestra la pila de llamadas: cada marco representa una función que está activa y permite entender qué función llamó a cuál. Es muy útil cuando una excepción aparece dentro de varias funciones anidadas. La línea que produce el error es el punto de detección; la causa puede estar en un valor calculado en un marco anterior.
4. Ejemplo completo: generar y buscar números¶
El ejemplo original de estos apuntes plantea una función que recibe una lista,
calcula su máximo y su mínimo, genera diez números aleatorios entre 1 y 100
y comprueba si otro número está en la lista. Es un buen caso para depurar porque
combina una función, un bucle, una validación de entrada y una búsqueda.
4.1. Código corregido para iniciar la depuración¶
El siguiente programa es ejecutable y conserva ese objetivo. La función
calcular_max_min() necesita una lista no vacía; el bucle garantiza que la lista
contenga diez elementos antes de llamarla.
#!/usr/bin/env python3
import random
def calcular_max_min(lista: list[int]) -> tuple[int, int]:
"""Devuelve el valor máximo y el mínimo de una lista no vacía."""
return max(lista), min(lista)
def pedir_numero() -> int:
"""Pide un número entero comprendido entre 1 y 100."""
valido = False
numero = 0
while not valido:
texto = input("Dime un número del 1 al 100: ")
try:
numero = int(texto)
except ValueError:
print("Debes introducir un número entero.")
else:
if 1 <= numero <= 100:
valido = True
else:
print("El número debe estar entre 1 y 100.")
return numero
numeros = []
# Inicializamos la lista con diez valores aleatorios entre 1 y 100.
for _ in range(10):
numeros.append(random.randint(1, 100))
vmax, vmin = calcular_max_min(numeros)
print("Lista generada:", numeros)
print(f"El valor máximo es {vmax}.")
print(f"El valor mínimo es {vmin}.")
numero = pedir_numero()
if numero in numeros:
print("El número está en la lista.")
else:
print("El número no está en la lista.")
Una ejecución concreta puede mostrar una lista distinta porque interviene la generación aleatoria. Para depurar una ejecución concreta, anota la lista generada o utiliza una semilla fija durante las pruebas. La aleatoriedad no es un error, pero dificulta comparar dos ejecuciones si no se controla.
4.2. Qué errores tenía el borrador inicial¶
El borrador se entrega como ejercicio de corrección y contiene errores intencionados. No se debe intentar depurar su flujo hasta corregir primero los errores que impiden analizarlo. Estos son los cambios importantes:
| Fragmento o problema | Tipo | Corrección |
|---|---|---|
range(0..10) |
Sintaxis | range(10), porque el límite final es exclusivo. |
randint(1, 1000) |
Lógica | randint(1, 100), según el intervalo pedido. |
Comillas tipográficas “ ” |
Sintaxis | Comillas normales de Python, como "texto". |
while numero > 0 or numero < 100 |
Lógica | Validar numero < 1 or numero > 100, o usar not 1 <= numero <= 100. |
Sangría ausente en el while y el if |
Sintaxis | Indentar el cuerpo cuatro espacios. |
if numero in numero |
Ejecución | Buscar en la lista: if numero in numeros. |
elsa |
Sintaxis | La palabra reservada correcta es else. |
CalcularMaxMin |
Estilo | calcular_max_min sigue la convención habitual de Python. |
La condición original numero > 0 or numero < 100 es especialmente importante:
para cualquier entero una de las dos comparaciones es verdadera, por lo que la
petición se repetiría indefinidamente. Para describir un número fuera
del intervalo se necesita que sea menor que 1 o mayor que 100.
Primero sintaxis; después comportamiento
Un depurador no puede avanzar por una instrucción que Python no consigue interpretar. Corrige primero la sintaxis y los nombres inexistentes; después utiliza el depurador para investigar excepciones y errores de lógica.
4.3. Sesión de depuración paso a paso¶
Podemos seguir esta secuencia con el programa corregido:
- Coloca un breakpoint en
numeros.append(...)y pulsa Start Debugging o F5. - Comprueba en Variables que
numeroscomienza como una lista vacía y que la próxima instrucción es la línea resaltada. - Pulsa F11 para ejecutar la siguiente instrucción. Al repetirlo, observa cómo la lista va incorporando números.
- Añade
len(numeros)ynumerosal panel Watch para seguir su tamaño y contenido. - Cuando llegues a
calcular_max_min(numeros), usa F10 si confías en la función o F11 si quieres entrar en ella y observarmax()ymin(). - Coloca un breakpoint en
if numero in numerosy comprueba en la consolanumero,numerosynumero in numeros. - Pulsa F5 para continuar hasta el siguiente breakpoint o hasta el final. Si una llamada no produce el resultado esperado, revisa el Call Stack.
La línea resaltada representa la próxima instrucción, no una instrucción que ya se haya ejecutado. Esta diferencia explica muchos errores al interpretar una captura del depurador: antes de pulsar el botón, el estado corresponde al punto anterior a esa línea.
5. Buenas prácticas¶
Para depurar con criterio profesional, conviene aplicar estas pautas:
- Reproduce el problema con una entrada concreta y conserva los datos relevantes antes de cambiar el código.
- Formula una hipótesis y cambia una sola cosa cada vez; así sabrás qué modificación ha resuelto el problema.
- Coloca breakpoints estratégicos en los cambios de estado, las decisiones y las llamadas a funciones, no en todas las líneas.
- Inspecciona valores y tipos: un valor aparentemente correcto puede ser una cadena cuando el programa necesita un entero.
- Usa
Step OveryStep Intocon intención: entra solo en el código que necesitas investigar y evita perderte en librerías ya conocidas. - Comprueba los límites: prueba listas vacías, un solo elemento, valores mínimos y máximos, entradas inválidas y funciones que no reciben datos.
- Verifica la corrección con pruebas y no solo con una ejecución manual que haya terminado sin excepciones.
- Retira
print()ybreakpoint()temporales antes de entregar el código; utilizaloggingcuando necesites un registro controlado. - No expongas información sensible en salidas, registros o capturas de pantalla, especialmente si el programa maneja contraseñas o datos personales.
6. Errores frecuentes¶
| Error frecuente | Por qué ocurre | Cómo evitarlo |
|---|---|---|
| Intentar iniciar el depurador con errores de sintaxis. | Python no puede construir el programa ejecutable. | Corrige primero la línea indicada por el editor o el intérprete. |
| Colocar un breakpoint en una rama que no se ejecuta. | La condición nunca se cumple con los datos actuales. | Usa datos que recorran esa rama o coloca un punto anterior. |
| Confundir la línea resaltada con la ya ejecutada. | El depurador marca la próxima instrucción. | Observa el estado antes y después de avanzar. |
| Entrar en todas las funciones con Step Into. | Se pierde tiempo en librerías o código que no se investiga. | Usa Step Over cuando la función ya esté comprobada. |
| Leer una variable fuera de su alcance. | La variable local solo existe en su marco de llamada. | Selecciona el marco adecuado en Call Stack o inspecciónala dentro de la función. |
| Corregir solo la excepción y no la causa. | Se interpreta el mensaje como el problema completo. | Revisa los valores y las decisiones que llevaron hasta la línea indicada. |
Usar or y and sin analizar la condición. |
La expresión puede ser verdadera siempre o nunca. | Prueba valores dentro, fuera y en los límites del intervalo. |
Dejar un breakpoint() o un print() en la entrega. |
El código de investigación se mezcla con el código final. | Haz una revisión final y ejecuta las pruebas sin instrumentación temporal. |
| Probar únicamente el camino correcto. | Los datos ideales no cubren errores ni límites. | Añade casos de excepción, extremos, vacío y valores inesperados. |
7. Resumen¶
En este tema has aprendido que:
- depurar es observar y razonar sobre el estado del programa, no cambiar código al azar;
- los errores de sintaxis, ejecución, lógica y configuración se investigan de forma diferente;
- un breakpoint permite detener la ejecución y
Step Into,Step Over,Step OutyContinuepermiten recorrerla con distinto nivel de detalle; - Variables, Watch, Debug Console y Call Stack muestran el estado y el camino que ha seguido la ejecución;
pdb,print(),logging,asserty las pruebas complementan al depurador;- toda corrección debe verificarse con el caso que fallaba y con casos límite.
Idea clave
Una buena depuración convierte un comportamiento inesperado en evidencias: se reproduce el problema, se observa el estado, se aísla la causa, se corrige con una modificación pequeña y se verifica el resultado.
8. Para seguir practicando¶
- Realiza la práctica de depuración del algoritmo burbuja: establece un breakpoint, sigue los índices y observa cómo cambia la lista.
- Captura en Variables el estado de una colección durante varias
iteraciones y añade
len(lista)al panel Watch. - Reproduce el ejemplo de los números, introduce una letra y explica el
ValueErrorantes de decidir cómo tratarlo. - Ejecuta un programa con
python -m pdby practica los comandosn,s,cyp expresión. - Añade pruebas para una función de máximo y mínimo que definan el comportamiento esperado para una lista normal, una lista de un elemento y una lista vacía.
Bibliografía y fuentes¶
- Documentación oficial de Python:
pdb. - Documentación oficial de Python:
breakpoint(). - Visual Studio Code: depuración de Python.
- Cómo depurar con Python, OpenWebinars.
- Depurar con
pdb, Real Python. - Normativa oficial del título de Desarrollo de Aplicaciones Web.
Presentación¶
Puedes repasar los conceptos principales con la presentación de depuración de la unidad 2.4.