Depurar en Python — teoría con la cuenta de la mesa 3, tracebacks, prints chivatos y pdb (y los 9,10 € que nadie debía)
Una tarde de estudio en la cafetería de la facultad, tres amigos pedimos tres cafés, dos bocadillos y un zumo, y me tocó repartir la cuenta. Como acababa de ver los operadores en FP1, lo hice con Python en el móvil, muy orgulloso:
# La cuenta de la mesa 3: tres amigos lo pagan todo a partes iguales
cafes = 3 * 1.20 # 3 cafés
bocadillos = 2 * 2.50 # 2 bocadillos
zumo = 1.50 # 1 zumo
amigos = 3
por_persona = cafes + bocadillos + zumo / amigos
print("Paga cada uno:", round(por_persona, 2), "€")
Salida:
Paga cada uno: 9.1 €
Les pedí 9,10 € a cada uno. Uno pagó sin mirar; el segundo empezó a sumar en voz alta: «tres cafés a 1,20, dos bocadillos a 2,50 y un zumo… eso son 10,10 en total, ¿y nos cobras 27,30?». Python no había dado ningún error. El programa hizo exactamente lo que le escribí; lo que le escribí no era lo que yo quería. Ese es el error más peligroso de todos, el que no avisa, y para encontrarlo hace falta depurar.
Depurar en Python es una de las habilidades más importantes de primero y casi nadie la enseña de forma directa: sabes escribir código, pero cuando algo falla (y siempre falla) no sabes por dónde empezar. Hoy aprendes los tres tipos de errores y cuándo aparece cada uno, a leer un traceback de abajo arriba, a poner prints chivatos (con un truco de las f-strings que te va a encantar) y a usar los depuradores que tenemos en FP1: Thonny, Python Tutor, el de VS Code y pdb, el de la terminal. Vienes de los operadores en Python, de donde sale el fallo de la mesa 3, y después vienen los condicionales en Python, donde todo esto empieza a ser imprescindible.
Tabla de Contenidos
¿Qué es depurar?
Depurar (en inglés, debug) es buscar la causa de un fallo en un programa y corregirla. Un bug es cualquier cosa que hace que el programa no se comporte como esperas: que se pare con un error, que se quede colgado o que dé un resultado equivocado sin decir nada.
¿Cómo sabes que hay un bug? Probando el programa con datos de los que ya conoces el resultado correcto. Eso se llama un caso de prueba: tres cafés, dos bocadillos y un zumo entre tres tienen que salir a 3,37 € por persona (10,10 / 3). Si el programa dice otra cosa, hay un fallo, aunque no haya ningún mensaje de error.
Todas las herramientas de depuración, por muy distintas que parezcan, se basan en tres funciones:
- Ejecución paso a paso: el programa ejecuta una línea y se para, esperando a que le digas que siga.
- Inspección de variables: mientras está parado, puedes ver el valor de cada variable para comprobar si es el que esperabas.
- Puntos de parada (breakpoints): marcas una línea y el programa corre normal hasta llegar a ella, sin obligarte a ir línea a línea desde el principio.
Antes de las herramientas, lo primero es reconocer qué tipo de error tienes delante, porque cada uno aparece en un momento distinto:
Errores de sintaxis (SyntaxError)
El código está mal escrito y Python ni siquiera empieza a ejecutarlo: lo detecta al leer el archivo. Por eso, si hay un error de sintaxis en la línea 50, no se ejecutan ni las 49 de antes.
total = 3 * 1.20
print("Total:", total # falta cerrar el paréntesis
print("Gracias")
Salida:
File "sintaxis.py", line 2
print("Total:", total # falta cerrar el paréntesis
^
SyntaxError: '(' was never closed
Fíjate en dos cosas. Primero, no se ejecuta nada: ni siquiera la línea 1 (total = 3 * 1.20), y por eso no sale ningún «Total». Segundo, el mensaje señala la línea 2, donde se abrió el paréntesis que nunca se cerró, aunque Python se diera cuenta al leer la línea 3. En versiones antiguas de Python el error salía en la línea siguiente al fallo; si alguna vez el mensaje te señala una línea que parece correcta, mira la de arriba.
El primo hermano es el error de sangría:
precio = 1.20 cantidad = 3 # dos espacios de más al principio
File "indentacion.py", line 2
cantidad = 3 # dos espacios de más al principio
IndentationError: unexpected indent
unexpected indent: hay una sangría donde no tocaba. En Python la sangría no es decoración, es parte de la sintaxis (dice qué líneas pertenecen a un if, a un bucle o a una función).
Los errores de sintaxis son los más fáciles de arreglar: el mensaje dice la línea y el ^ apunta casi al carácter. Los más comunes en FP1: dos puntos que faltan al final de if, for, while o def, paréntesis o comillas sin cerrar y sangrías mezcladas.
Errores de tipo (TypeError, ValueError)
El código está bien escrito, Python empieza a ejecutarlo y, al llegar a una línea concreta, se encuentra algo que no puede hacer. Se para en esa línea y te dice qué pasó. Se llaman errores en tiempo de ejecución o excepciones. Los dos que más vas a ver tienen que ver con los tipos:
cantidad = "3" # llega como texto, igual que con input() total = cantidad * 1.20
Traceback (most recent call last):
File "tipo.py", line 2, in <module>
total = cantidad * 1.20
~~~~~~~~~^~~~~~
TypeError: can't multiply sequence by non-int of type 'float'
La cantidad llegó como texto "3" (igual que llega todo lo que lee input()). Multiplicar un texto por un entero sí se puede (repite el texto), pero por un decimal no: can't multiply sequence by non-int of type 'float'. El arreglo es convertir antes: int(cantidad) * 1.20.
cafes = 3
print("Cafés: " + cafes)
Traceback (most recent call last):
File "concatenar.py", line 2, in <module>
print("Cafés: " + cafes)
~~~~~~~~~~^~~~~~~
TypeError: can only concatenate str (not "int") to str
Aquí es al revés: intentas pegar (concatenar) un texto con un número. El + entre textos pega y entre números suma, pero no sabe mezclar. Arreglos: "Cafés: " + str(cafes), print("Cafés:", cafes) o una f-string, f"Cafés: {cafes}".
cantidad = int("tres")
Traceback (most recent call last):
File "valor.py", line 1, in <module>
cantidad = int("tres")
ValueError: invalid literal for int() with base 10: 'tres'
ValueError es otro tipo de queja: el tipo del dato es el correcto (int() acepta textos), pero el valor no se puede convertir. int("3") funciona; int("tres"), no. Cuando un usuario teclea algo raro en un input(), este es el error que verás.
En Python 3.11 y siguientes, los tracebacks subrayan con ~~~^~~~ la operación exacta que falló dentro de la línea. Si tu Python es más antiguo, verás el mismo mensaje sin subrayado.
Errores silenciosos
El más peligroso: el programa termina sin ningún error y el resultado está mal. Python no avisa porque, para él, no hay nada raro: hizo lo que le escribiste. Es el caso de la mesa 3.
¿Qué pasó? Los operadores tienen precedencia: la división / se hace antes que la suma +, igual que en matemáticas. Así que cafes + bocadillos + zumo / amigos no reparte la cuenta: divide solo el zumo entre tres y suma el resto entero.
Con números: 3.6 + 5.0 + 1.5 / 3 = 3.6 + 5.0 + 0.5 = 9.1. Lo que yo quería era (3.6 + 5.0 + 1.5) / 3 = 10.1 / 3 = 3.37. Faltaban unos paréntesis, y ningún mensaje me lo dijo. Para estos errores no hay traceback que leer: hay que comprobar los resultados con un caso de prueba que sepas hacer a mano y, cuando no cuadra, mirar los valores intermedios. Ahí entran las herramientas.
Dar por bueno un resultado porque el programa «no ha dado error». Que no haya error solo significa que Python pudo hacer las operaciones, no que sean las que querías.
Cómo leer un traceback
Cuando un programa se para con un error, Python escribe un traceback: el informe de lo que estaba haciendo cuando falló. Asusta un poco las primeras veces, pero siempre tiene la misma forma. Mira este:
cantidad_txt = "3" # el dato llega como texto (como con input()) precio = 1.20 subtotal = cantidad_txt * 2 # no falla: "3" * 2 es "33" total = subtotal * precio # falla aquí... pero la causa está dos líneas más arriba print(total)
Salida:
Traceback (most recent call last):
File "traceback.py", line 4, in <module>
total = subtotal * precio # falla aquí... pero la causa está dos líneas más arriba
TypeError: can't multiply sequence by non-int of type 'float'
Se lee de abajo arriba:
- La última línea dice QUÉ pasó: el tipo de error (
TypeError) y un mensaje. Léela entera antes que nada; muchas veces ya te dice la solución. - La línea
Filedice DÓNDE: el archivo (traceback.py) y el número de línea (4). - Debajo de
Fileaparece la línea que falló, copiada del código, para que no tengas que buscarla. - La primera línea,
Traceback (most recent call last):, solo anuncia el informe: «lo más reciente, al final».
Y ahora lo importante: la línea 4 es donde explotó, pero no donde está la causa. El problema empezó en la línea 3: cantidad_txt * 2 no dio error porque multiplicar un texto por un entero repite el texto, y subtotal acabó valiendo "33". La línea 4 solo fue la primera que no pudo con ese texto. Cuando la línea del traceback parezca correcta, pregúntate: ¿de dónde viene cada valor que se usa aquí? y sube.
Cuando aprendas funciones, los tracebacks tendrán varios bloques File, uno por cada función que estaba esperando a otra. Se leen igual: la última línea dice qué, el último File dice dónde explotó y los de encima cuentan cómo llegaste ahí. Lo verás en la teoría de funciones en Python.
Los prints chivatos (y el truco del =)
La herramienta más sencilla y la primera que deberías usar: añadir print() temporales que te «chivan» el valor de las variables en los puntos clave. Las f-strings tienen un truco pensado justo para esto: si escribes {variable=}, imprimen el nombre y el valor.
cafes = 3 * 1.20
bocadillos = 2 * 2.50
zumo = 1.50
amigos = 3
print(f"{cafes=} {bocadillos=} {zumo=}") # chivato 1: ¿los datos son los que creo?
por_persona = cafes + bocadillos + zumo / amigos
print(f"{por_persona=}") # chivato 2: ¿y el resultado?
print(f"{zumo / amigos=}") # chivato 3: ¿qué se está dividiendo?
por_persona = (cafes + bocadillos + zumo) / amigos # el arreglo: paréntesis
print("Paga cada uno:", round(por_persona, 2), "€")
Salida:
cafes=3.5999999999999996 bocadillos=5.0 zumo=1.5 por_persona=9.1 zumo / amigos=0.5 Paga cada uno: 3.37 €
Cada chivato responde una pregunta:
- ¿Los datos son los que creo? Sí: cafés, bocadillos y zumo están bien. (Ese
3.5999999999999996no es el bug: es la imprecisión normal de los decimales en binario, que viste en variables y tipos de datos. Por eso se redondea al final.) - ¿El resultado es el que espero? No: 9.1, cuando a mano sale 3.37.
- ¿Qué se está dividiendo?
zumo / amigos=0.5: solo el zumo. Encontrado.
Con el arreglo de los paréntesis sale 3.37 €. (Si cobras 3,37 € a cada uno, juntas 10,11 €: el céntimo de más es el redondeo de repartir 10,10 entre tres, no un bug.)
Dejar los prints chivatos en el código que entregas. Quítalos al terminar, o mejor: cuando el programa crezca, usa un depurador, que no ensucia el código.
Herramienta 1: Thonny
Thonny es un editor de Python pensado para quien empieza, y su depurador es el más visual de todos. Se descarga gratis en thonny.org. Para depurar, en lugar del botón de ejecutar normal pulsa el del insecto (Debug current script):

La línea que se va a ejecutar se resalta, y en la barra aparecen los controles:
- Step over (F6): ejecuta la línea actual entera y se para en la siguiente.
- Step into (F7): si la línea llama a una función, entra dentro y se para en su primera línea.
- Step out: termina la función en la que estás y se para al volver.
- Resume: sigue sin pararse hasta el siguiente punto de parada (o hasta el final).
Para poner un punto de parada, haz clic en el número de la línea: aparece un punto rojo. Lo más útil de Thonny es el panel de Variables (menú View → Variables), que enseña el nombre y el valor de cada variable mientras el programa avanza:

Con el programa de la mesa 3, avanzando con Step over, verías aparecer cafes, bocadillos, zumo y amigos con sus valores y, al pasar la línea del cálculo, por_persona con un 9.1 que no cuadra.
En View → Heap Thonny enseña además la memoria donde viven los datos, y el panel de variables pasa a mostrar direcciones en lugar de valores. Para empezar, deja el Heap cerrado.
Herramienta 2: Python Tutor
Python Tutor es una web que ejecuta tu código paso a paso y dibuja las variables. No hay que instalar nada: pegas el código, eliges Python 3 y pulsas Visualize Execution.

A la izquierda está el código con dos flechas: la gris señala la línea que se acaba de ejecutar y la roja, la siguiente. Debajo, los botones First, Prev, Next y Last (y una barra para moverte), así que puedes ir hacia atrás, algo que los depuradores normales no hacen. A la derecha, arriba, la salida del programa y, debajo, todas las variables con su valor.
Es perfecto para programas cortos y para errores silenciosos: avanzas, miras cada valor y ves en qué paso la cuenta se tuerce. Tiene límites: el programa tiene que caber en un solo archivo y no puede ser muy largo. Puedes poner puntos de parada haciendo clic en una línea. Al final de este artículo tienes un programa preparado para probarlo.
Si tu programa usa input(), Python Tutor te pedirá los datos en una caja debajo de los botones. Para depurar más rápido, cambia los input() por valores fijos mientras buscas el fallo.
Herramienta 3: el depurador de VS Code
Si programas en Visual Studio Code con la extensión de Python, ya tienes un depurador muy completo. Haz clic a la izquierda del número de la línea donde quieras parar: aparece un punto rojo, el breakpoint.

Después pulsa F5 (o Ejecutar → Iniciar depuración) y, si te pregunta, elige depurar el archivo de Python actual. El programa corre hasta el breakpoint y se para antes de ejecutar esa línea. En el panel de la izquierda tienes:
- Variables: todas las variables activas con su valor en ese momento.
- Inspección (Watch): expresiones que tú escribes y que se recalculan en cada paso. Con
zumo / amigoshabrías visto el 0.5 al instante. - Pila de llamadas (Call Stack): en qué función estás y desde dónde se llamó. Te será muy útil cuando llegues a las funciones.
Los controles de la barra que aparece arriba:
| Tecla | Acción | Qué hace |
|---|---|---|
| F5 | Continuar | sigue hasta el siguiente breakpoint |
| F10 | Paso a paso por procedimientos (Step Over) | ejecuta la línea entera |
| F11 | Paso a paso por instrucciones (Step Into) | entra en la función de esa línea |
| Mayús + F11 | Salir (Step Out) | termina la función actual y vuelve |
| Mayús + F5 | Detener | para la depuración |
En lugar de un breakpoint normal, haz clic derecho en el punto rojo y elige Editar punto de interrupción → Expresión: el programa solo se parará cuando la condición sea cierta (por ejemplo, amigos == 0). Con bucles largos es oro.
Herramienta 4: pdb, el depurador de la terminal
Los apuntes de FP1 también enseñan pdb (Python debugger), que viene con Python y funciona en cualquier terminal, sin editor. La forma más fácil de usarlo es escribir breakpoint() en la línea donde quieras parar:
cafes = 3 * 1.20
bocadillos = 2 * 2.50
zumo = 1.50
amigos = 3
breakpoint() # el programa se para aquí y abre pdb
por_persona = cafes + bocadillos + zumo / amigos
print("Paga cada uno:", round(por_persona, 2), "€")
Al ejecutarlo, el programa se para y aparece el indicador (Pdb), esperando órdenes. Esta es la sesión real, con las órdenes que tecleé:
> ticket_pdb.py(5)<module>()
-> breakpoint() # el programa se para aquí y abre pdb
(Pdb) p cafes, bocadillos, zumo
(3.5999999999999996, 5.0, 1.5)
(Pdb) n
> ticket_pdb.py(6)<module>()
-> por_persona = cafes + bocadillos + zumo / amigos
(Pdb) n
> ticket_pdb.py(7)<module>()
-> print("Paga cada uno:", round(por_persona, 2), "€")
(Pdb) p por_persona
9.1
(Pdb) p zumo / amigos
0.5
(Pdb) c
Paga cada uno: 9.1 €
Qué hace cada orden de la sesión:
| Orden | Qué hace | En la sesión |
|---|---|---|
p expresión | imprime el valor de una variable o expresión | p cafes, bocadillos, zumo enseña los tres datos |
n (next) | ejecuta la línea actual y se para en la siguiente | dos n avanzan hasta la línea del print |
p por_persona | — | 9.1: el fallo |
p zumo / amigos | — | 0.5: la causa |
c (continue) | sigue hasta el final o el siguiente punto de parada | el programa termina e imprime |
Las líneas que empiezan por > dicen dónde está parado (archivo, línea y <module>, que significa «el programa principal») y las que empiezan por -> enseñan la línea que se va a ejecutar. Otras órdenes útiles: l (list) enseña el código alrededor, s (step) entra en una función, b 7 pone un punto de parada en la línea 7 y q sale.
Desde Python 3.13, breakpoint() se para en su propia línea (por eso la sesión empieza en la 5 y hacen falta dos n); en versiones anteriores se paraba ya en la siguiente. En los apuntes verás la forma antigua, import pdb y pdb.set_trace(), que hace lo mismo.
Mi flujo de depuración en la práctica
Después de un curso entero peleándome con errores, este es el orden que sigo:
- Leo el error entero, de abajo arriba. La última línea dice qué pasó y el
Filedice dónde. No busco nada en Google antes de haberlo leído. - Me hago un caso de prueba que sepa resolver a mano. Si no sé cuál debería ser el resultado, no puedo saber si está mal. Para la mesa 3: 10,10 / 3 = 3,37.
- Pongo prints chivatos con
{variable=}en los puntos clave para ver dónde se tuerce el valor. Con eso se resuelven la mayoría de los errores de primero. - Si no lo encuentro, paso a paso. Python Tutor o Thonny para programas cortos; el depurador de VS Code (o
pdb) para programas largos, con breakpoints donde sospecho. - Si el mensaje no me dice nada, lo busco. Copio la última línea del traceback (sin mis nombres de variables) y la busco. Casi todos los errores de primero ya los ha tenido alguien antes.
Cuando arregles un error, vuelve a probar todos los casos de prueba, no solo el que fallaba. Un arreglo puede romper otra cosa.
Los mensajes de error más comunes en FP1
Todos estos mensajes son de Python 3.13 y, salvo el de los dos puntos, salen tal cual de los programas de este artículo. Guárdate la tabla:
| Mensaje | Qué significa | Cómo se arregla |
|---|---|---|
SyntaxError: '(' was never closed | un paréntesis abierto sin cerrar | cierra el paréntesis (mira también la línea de arriba) |
SyntaxError: expected ':' | faltan los dos puntos de un if, for, while o def | añade : al final de la línea |
IndentationError: unexpected indent | una sangría donde no toca | alinea la línea con las de su bloque |
TypeError: can only concatenate str (not "int") to str | + entre texto y número | str(numero) o una f-string |
TypeError: can't multiply sequence by non-int of type 'float' | multiplicas un texto por un decimal | convierte el texto con int() o float() |
ValueError: invalid literal for int() with base 10: 'tres' | el texto no representa un número | comprueba lo que llega antes de convertir |
NameError: name 'precio_cafee' is not defined. Did you mean: 'precio_cafe'? | una variable que no existe (casi siempre, una errata) | corrige el nombre: Python te sugiere el bueno |
ZeroDivisionError: float division by zero | divides entre 0 | comprueba el divisor antes de dividir |
Los tres últimos, con sus programas:
precio_cafe = 1.20 total = 3 * precio_cafee
Traceback (most recent call last):
File "nombre.py", line 2, in <module>
total = 3 * precio_cafee
^^^^^^^^^^^^
NameError: name 'precio_cafee' is not defined. Did you mean: 'precio_cafe'?
total = 10.10 amigos = 0 print(total / amigos)
Traceback (most recent call last):
File "cero.py", line 3, in <module>
print(total / amigos)
~~~~~~^~~~~~~~
ZeroDivisionError: float division by zero
Fíjate en el Did you mean: 'precio_cafe'?: desde Python 3.10, si escribes mal el nombre de una variable, Python busca la más parecida y te la sugiere.
Míralo paso a paso en Python Tutor
Para terminar, vamos a cazar el bug de la mesa 3 con Python Tutor. Elige Python 3, pega este programa (es la cuenta partida en pasos) y avanza con Next:
cafes = 3 * 1.20
bocadillos = 2 * 2.50
zumo = 1.50
amigos = 3
parte_zumo = zumo / amigos
mal = cafes + bocadillos + parte_zumo
bien = (cafes + bocadillos + zumo) / amigos
print("mal:", round(mal, 2))
print("bien:", round(bien, 2))
Lo que debería salir al final:
mal: 9.1 bien: 3.37
Qué observar paso a paso:
- Tras la primera línea,
cafesaparece en el Global frame con3.5999999999999996. No es el bug: es la imprecisión de los decimales, y elrounddel final la esconde. - En la línea de
parte_zumo, fíjate en que vale0.5: es la división que, en la versión del gancho, Python hacía sola sin preguntarte. - Compara
mal(9.1, con 0.5 de zumo) ybien(3.3666…, con todo dividido). La diferencia está a la vista, variable a variable. - Usa Prev para volver atrás y fíjate en que, hasta la línea de
mal, todos los valores eran correctos: el fallo está en una sola operación. Eso es depurar: encontrar el primer paso en el que un valor deja de ser el que esperabas.
Resumen: cuándo usar cada herramienta
| Situación | Herramienta |
|---|---|
SyntaxError o IndentationError | lee el mensaje: línea y ^; si la línea parece bien, mira la de arriba |
Error en ejecución (TypeError, ValueError, NameError…) | lee el traceback de abajo arriba y pregúntate de dónde viene cada valor |
| Error silencioso en un programa corto | caso de prueba + prints chivatos con {variable=} o Python Tutor |
| Error silencioso en un programa largo | depurador de VS Code con breakpoints (o condicionales) |
| Sin editor, solo terminal | breakpoint() y pdb (p, n, s, c, q) |
| Entender cómo se ejecuta algo nuevo | Thonny o Python Tutor paso a paso, aunque no haya error |
Tres ideas para llevarte:
- Un programa sin errores no es un programa correcto: compruébalo con un caso de prueba que sepas hacer a mano.
- El traceback se lee de abajo arriba, y la causa puede estar unas líneas antes de donde explotó.
- Depurar es encontrar el primer valor que no es el que esperabas: prints chivatos o paso a paso, pero mirando valores, no adivinando.
En el siguiente tema, los condicionales en Python, tus programas empezarán a tomar decisiones, y los errores silenciosos se esconderán en la rama que no esperabas. Todo lo que has visto hoy te va a hacer falta.
Sigue aprendiendoTodos los artículos de Python¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




Qué es un sistema operativo — teoría con tu portátil en una tarde de estudio, una llamada a write() y la CPU que nunca descansa (y los tres programas que no se ejecutaban a la vez)