Leer un Traceback (El Parte de Accidente)

Fig. 10.2: Un traceback se lee de abajo hacia arriba.
Atrapar errores con try es media historia. La otra media es saber leer el accidente cuando ocurre, porque muchísimos errores no se arreglan con un try: se arreglan entendiendo qué te está diciendo Python. Cuando algo falla, no se queda callado: imprime un traceback, un parte de accidente detallado. La mayoría de los principiantes lo ven, se asustan por el color rojo y lo ignoran, y se equivocan: ese texto te dice exactamente qué pasó y dónde.
Anatomía del Parte
Mira un traceback real:
Traceback (most recent call last):
File "almacen.py", line 7, in <module>
total = precio * cantidad
~~~~~~~^~~~~~~~~~
TypeError: can't multiply sequence by non-int of type 'str'
Se lee de abajo hacia arriba (ver Fig. 10.2), y la regla de oro es: la última línea es la que importa. Ahí está el tipo de error (TypeError) y el mensaje, siempre en inglés, de qué salió mal. Python no reporta en otro idioma, y en el trabajo profesional tampoco lo hará: conviene acostumbrarse a leerlo desde ahora. Este dice “can’t multiply sequence by non-int of type ‘str’”: no se puede multiplicar una secuencia (aquí, un texto) por algo que no sea un número entero. En otras palabras, intentaste texto * texto. Lo de arriba es el rastro de cómo llegó tu programa hasta el punto del choque.
Desde Python 3.11, además, esas rayitas señalan la expresión exacta que reventó, no solo la línea: las virgulillas (~) cubren los operandos y el ^ marca el operador que falló. Ya no tienes que adivinar cuál de tres operaciones en la misma línea falló: Python te lo subraya. Si tu terminal corre en el piso mínimo del libro (Python 3.10), no verás esta línea de marcas: el traceback se corta después del código y va directo al TypeError. Es lo esperado, no una señal de que algo está roto.
Los Accidentes Más Comunes
Estos cinco cubren la gran mayoría de lo que verás al empezar. Aprende a reconocerlos por su nombre:
| El nombre | Qué significa | Causa típica |
|---|---|---|
NameError | Usaste un nombre que no existe | una variable mal escrita o sin definir |
TypeError | Mezclaste tipos incompatibles | "50" * "2", sumar texto y número |
IndexError | Pediste una posición que no existe | lista[10] en una lista de 3 |
KeyError | Pediste una clave que no existe | dic["edad"] sin esa clave |
SyntaxError / IndentationError | Python no pudo ni leer tu código | un : que falta, una sangría torcida |
Python te Sugiere la Solución
Las versiones modernas (Python 3.10 en adelante) hacen algo casi servicial: cuando te equivocas en un nombre por poco, te proponen el correcto.
NameError: name 'lndice' is not defined. Did you mean: 'indice'?
(“El nombre ‘lndice’ no está definido. ¿Quisiste decir: ‘indice’?”; Python 3.10+)
Y desde 3.12, si olvidaste importar un módulo de la biblioteca estándar, también te avisa:
NameError: name 'math' is not defined. Did you forget to import 'math'?
(“¿Olvidaste importar ‘math’?”; Python 3.12+)
Léelo como lo que es: una pista del propio intérprete. Antes de buscar en internet, lee la última línea completa. La mitad de las veces, la respuesta ya está ahí.
Acorralando al Bug
Leer el parte de accidente es la mitad del oficio. La otra mitad es cuando no hay accidente: el programa corre completo y entrega un resultado equivocado. Nada revienta, nada avisa, y aún así algo está mal. Para ese caso hay dos herramientas y una técnica.
El print táctico
El instinto de meter print por todos lados es correcto; lo que falla es la puntería. Un print táctico imprime el nombre y el valor juntos, en la frontera donde sospechas que el dato se corrompe:
total = calcular_total(carrito)
print(f"DEBUG total={total!r} tipo={type(total)}") # ¿qué llegó de verdad?
Dos detalles de puntería: el !r muestra el valor crudo (con comillas, con espacios visibles: '50 ' en vez de 50, que es como se descubre el clásico espacio colado), y type() desenmascara al "50" que creías número. La técnica: pon el print DESPUÉS de la línea sospechosa, mira, y muévelo media función más arriba o abajo según lo que veas. Es una búsqueda dirigida, no una alfombra de prints regados por todo el código. Y cuando el bug muera, los prints de DEBUG se van: eran andamios temporales.
breakpoint(): congelar la escena
Cuando los prints no alcanzan, Python puede pausar el programa a mitad de ejecución. Escribe breakpoint() en la línea donde quieres detenerte y ejecuta normal:
def procesar(lote: list) -> int:
total = 0
for caja in lote:
breakpoint() # el programa se detiene AQUÍ, en cada vuelta
total += caja
return total
El programa se detiene ahí y te deja en una consola justo en ese punto, con todas las variables vivas a la mano. Cuatro comandos te bastan: p variable imprime cualquier variable, n avanza una línea, c continúa hasta el siguiente breakpoint, y q abandona. Cuando termines, borra la línea. (En la edición web esta pausa no funciona: es una herramienta de terminal.)
El método
Ni el print ni el breakpoint piensan por ti. La técnica que los vuelve útiles es vieja y confiable: formula una hipótesis antes de mirar. “Apuesto a que precio llega como texto.” Luego verifica. Si acertaste, ya sabes qué arreglar; si no, acabas de descartar una causa y la siguiente hipótesis es mejor. Sin hipótesis, mueves prints al azar esperando un milagro; con una, cada print te acerca a la falla.
Con esto, un traceback deja de ser un muro rojo y pasa a ser lo que de verdad es: la ruta más corta hacia el error. Ahora, a los ejercicios: provoca unos cuantos a propósito y practica leerlos.
Resumen del Capítulo
try/exceptintenta una operación riesgosa con un plan B listo; puedes encadenar variosexceptpara distintos tipos de accidente.- Excepciones comunes:
ValueError,TypeError,KeyError,FileNotFoundError,ZeroDivisionError; todas descienden deException. except Tipo as ete da el mensaje exacto del error;finallyejecuta su bloque siempre, haya o no excepción.raise ValueError("mensaje")lanza tu propia alerta cuando un dato viola las reglas del programa, en vez de dejar que falle más adelante.- Atrapa solo los errores previsibles que sabes manejar; nunca uses un
except:a secas, que esconde hasta tus propios bugs. - Un traceback se lee de abajo hacia arriba: la última línea trae el tipo de error y su mensaje.
- El
printtáctico (con!rytype()) ybreakpoint()ayudan a acorralar un bug cuando el programa no truena, solo calcula mal.