100 Días de Código en Python — Día 13: Depuración — Cómo Encontrar y Corregir Errores
Nivel: Grado décimo
Área: Tecnología e Informática
Modalidad: Tutorial práctico
Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 13.
20 min de lectura
16 secciones
Este tutorial adapta pedagógicamente el Día 13. El curso presenta 10 consejos de depuración; aquí se organizan en 7 temas. Las secciones 📝 Ejercicio original del curso provienen de los archivos del curso. El Día 13 no tiene proyecto final propio y sus retos de depuración finales no están incluidos entre los archivos suministrados. Por eso el proyecto del día, el proyecto extra, el integrador y los retos 🟢🟡🟠🔴 son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.
(Fuentes: 094 - Describe the Problem.md, parte inicial, y 101 - Building Confidence.md)
Depurar (debugging) es encontrar y eliminar errores (bugs) del código. El curso recuerda la historia de Grace Hopper, pionera de la programación: en un registro de trabajo de su equipo quedó pegada una polilla que se había encontrado en un relé del computador y que impedía que funcionara bien.
La idea central del día: no te deprimas por crear bugs. Todos los programadores los creamos. Lo que se entrena es la habilidad de encontrarlos y corregirlos. Como dice el curso, cada bug que resuelves es una repetición en el gimnasio.
Mensaje de cierre: la confianza se gana superando obstáculos. Cada problema difícil que resuelves hace que el siguiente parezca más fácil.
Prerequisito general: Días 1 a 12. Hoy repasarás muchos errores de tu cuaderno de errores.
"Si el problema es confuso en tu cabeza, es casi imposible depurarlo." Antes de tocar el código, responde:
¿Qué debería hacer el programa?
¿Qué hace realmente?
¿Qué supongo que es cierto? ¿Lo es de verdad?
📝 Ejercicio original del curso: "Lo tienes" nunca aparece#
Python
123456
defmy_function():for i inrange(1,20):if i ==20:print("Lo tienes")
my_function()
Descripción del problema (según el curso): el bucle recorre números desde el 1 y se espera que imprima "Lo tienes" cuando i llegue a 20, pero nunca lo hace.
Suposición falsa: que i llega a 20. range(1, 20) va del 1 al 19, porque el límite superior no se incluye (Día 5). Comprobado: 20 in range(1, 20) es False.
Corrección:range(1, 21).
Los bugs que aparecen solo a veces son los más difíciles: pruebas dos veces, todo sale bien… y en la tercera falla. El truco es encontrar exactamente cuándo falla y forzar esa situación.
📝 Ejercicio original del curso: el dado que a veces falla#
Python
1234
from random import randint
dice_images =["❶","❷","❸","❹","❺","❻"]
dice_num = randint(1,6)print(dice_images[dice_num])
Estrategia del curso:
Revisar la documentación: randint(1, 6)incluye el 1 y el 6.
Fijar el valor para probar: dice_num = 1, luego 2, 3… y 6.
Con dice_num = 6siempre aparece IndexError: list index out of range. ¡Bug reproducido!
Causa: los índices van de 0 a 5. Corrección:randint(0, 5).
Recordatorio del Día 4: random.choice(dice_images) evita este problema por completo.
🟢 Reto 1: este programa falla "de vez en cuando". Reprodúcelo fijando el valor aleatorio y corrígelo:
Python
123
import random
meses =["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"]print(meses[random.randint(1,12)])
🟡 Reto 2: investiga random.seed(n) y úsalo para que una secuencia aleatoria se repita siempre igual. ¿Cómo ayuda eso a reproducir un bug?
🟠 Reto 3: un programa falla solo cuando el usuario escribe su nombre con tilde. ¿Cómo lo reproducirías y qué datos de prueba usarías?
🔴 Reto 4: diseña una tabla de datos de prueba límite (vacío, 0, negativo, máximo, con espacios, con mayúsculas) para la Calculadora del Día 10. ¿Cuáles reproducen un error?
Ejecutar el código "a mano", línea por línea, evaluando cada condición.
📝 Ejercicio original del curso: ¿Millennial o Generación Z?#
Python
12345
year =int(input("¿En qué año naciste? "))if year >1980and year <1994:print("Eres millennial.")elif year >1994:print("Eres de la Generación Z.")
Síntoma: con 1994 no imprime nada (verificado).
Jugando a ser el computador (como el curso):
Paso
Evaluación
Resultado
1994 > 1980
True
—
1994 < 1994
False
True and False → False: se salta el if
1994 > 1994
False
Se salta el elif
—
No hay else
No se imprime nada
Corrección: que un extremo incluya el 1994, con <= o con >=, según la clasificación que se quiera usar.
🛠️ Nota: los rangos de años de cada "generación" varían según la fuente (el propio curso aclara que dependen del país). Por ejemplo, el Pew Research Center considera millennials a los nacidos entre 1981 y 1996. Para la programación lo importante es que no queden años sin cubrir.
Un print sin sangría después de if → expected an indented block
Subrayado rojo antes de ejecutar
Error en tiempo de ejecución
int("Twelve") → ValueError: invalid literal for int() with base 10: 'Twelve' (verificado)
Aparece en la consola solo con ciertas entradas
Error de lógica
print("Puedes manejar a los {age} años") imprime literalmente {age}
No hay ningún mensaje: el programa hace algo distinto a lo esperado
Buscar el error en internet (técnica del curso): copia solo la parte genérica del mensaje (ValueError: invalid literal for int() with base 10), sin los datos propios de tu programa ('Twelve'), y agrega "Python".
Primer contacto con try/except. El curso lo presenta aquí por primera vez:
Python
12345
try:
age =int(input("¿Cuántos años tienes? "))except ValueError:print("Escribiste un número inválido. Intenta con un número como 15.")
age =int(input("¿Cuántos años tienes? "))
try: "intenta esta línea potencialmente peligrosa".
except ValueError: "si ocurre este error, en lugar de detener el programa, haz esto otro".
🛠️ Nota técnica: en la solución del curso, la segunda pregunta está dentro del except, sin protección. Si el usuario vuelve a escribir un texto, el programa se detiene con ValueError. La solución robusta combina while y try/except, y se trabaja en el proyecto integrador:
Python
123456
whileTrue:try:
age =int(input("¿Cuántos años tienes? "))breakexcept ValueError:print("Escribe un número, por ejemplo 15.")
(break sale del bucle; investígalo en la documentación.)
Los bugs más frustrantes no generan errores: "nadie te ayuda en esto". Ahí cuenta la experiencia. El curso anima a ayudar a otros en foros para practicar resolviendo bugs ajenos.
🟢 Reto 1: provoca a propósito un ValueError, un TypeError, un IndexError, un KeyError y un NameError. Escribe para cada uno la parte "buscable" del mensaje.
🟡 Reto 2: protege con try/except la conversión de la oferta en la Subasta Secreta (Día 9).
🟠 Reto 3: en la Calculadora (Día 10), atrapa ZeroDivisionError y KeyError con dos bloques except distintos, cada uno con su mensaje.
🔴 Reto 4: escribe tres bugs de lógica, sin mensaje de error, en un programa corto y dáselo a un compañero para que los encuentre.
Cuando el resultado es incorrecto, imprime los valores intermedios para ver dónde se desvían de lo esperado.
📝 Ejercicio original del curso: palabras en un libro#
Python
123456
pages =0
word_per_page =0
pages =int(input("Número de páginas: "))
word_per_page ==int(input("Número de palabras por página: "))
total_words = pages * word_per_page
print(total_words)
Síntoma: con 45 páginas y 250 palabras por página, el resultado es 0.
Técnica del curso: agregar print(f"pages = {pages}") y print(f"word_per_page = {word_per_page}"). Resultado: pages = 45, pero word_per_page = 0.
Causa:== compara en lugar de asignar. La comparación da False y la variable no cambia (verificado).
Corrección: usar =. El resultado correcto es 11 250.
🟢 Reto 1: encuentra con print por qué esta función siempre devuelve 1:
Python
123456
defcontar_vocales(texto):
contador =0for letra in texto:if letra in"aeiou":
contador =1return contador
🟡 Reto 2: agrega print dentro del while de tu juego Adivina el Número para mostrar turns en cada vuelta.
🟠 Reto 3: crea una banderaDEBUG = True (una constante, Día 12) y haz que los print de depuración solo aparezcan si DEBUG es True. Así los desactivas cambiando una sola línea.
🔴 Reto 4: investiga el módulo logging de Python. ¿Qué ventajas tiene frente a print en un programa grande?
Punto de interrupción (clic en el margen, junto al número de línea)
Pausa el programa en esa línea
Debug (ícono del insecto)
Ejecuta el programa en modo de depuración
Step Over
Ejecuta la línea actual y se detiene en la siguiente
Step Into
Si la línea llama a una función, entra en ella, aunque sea de una librería como random
Step Into My Code
Entra solo en funciones escritas por ti e ignora las de las librerías
Step Out
Termina la función actual y vuelve a quien la llamó
Panel de variables
Muestra el valor de cada variable en ese momento
Silenciar puntos de interrupción
Ejecuta sin pausar
El curso lo resume así: el depurador es "como poner un print en todas las variables" y poder detener el tiempo en cualquier momento.
Curiosidad que muestra el curso: con Step Into sobre randint se ve que por dentro usa otra función, randrange. El depurador permite leer cómo está hecho el código de otros.
from random import randint
from maths import add # módulo propio con add(n1, n2)defmutate(a_list):
b_list =[]
new_item =0for item in a_list:
new_item = item *2
new_item += randint(1,3)
new_item = add(new_item, item)
b_list.append(new_item)print(b_list)
mutate([1,2,3,5,8,13])
Síntoma: se espera una lista de 6 números, pero se imprime uno solo (verificado).
Técnica del curso: poner un punto de interrupción, observar b_list en el panel de variables y notar que no cambia durante el bucle. Solo recibe un elemento al final.
Causa:b_list.append(new_item) está fuera del for, por un problema de sangría.
Corrección: darle sangría para meterlo dentro del bucle.
🛠️ Nota técnica: el módulo propio del curso se llama maths.py, con "s". No lo llames math.py: Python ya tiene un módulo math y el tuyo lo "taparía", lo mismo que pasaba con random.py en el Día 4. El archivo maths.py no está incluido entre los materiales suministrados: créalo con def add(n1, n2): return n1 + n2.
🟢 Reto 1: usa el depurador para seguir tu Blackjack con la mano [11, 11, 10] y observa cómo cambia la lista dentro de calculate_score.
🟡 Reto 2: pon un punto de interrupción dentro de check_answer (Día 12) y usa Step Out para volver a game().
🟠 Reto 3: usa Step Into sobre random.choice y describe qué encuentras.
🔴 Reto 4: investiga los puntos de interrupción condicionales (se detienen solo si se cumple una condición, por ejemplo i == 15). Úsalos en un bucle de 100 vueltas.
Mirar el código fijamente no revela la solución. Un té, una siesta o dormir: al volver, todo parece más obvio
8
Pregúntale a un amigo
Una persona real (idealmente alguien que programe o esté en el curso). No comparte tus suposiciones, así que ve lo que tú no ves. No es vergonzoso; la próxima vez tú le ayudas
9
Ejecuta a menudo
Prueba después de cada cambio pequeño. Si hay varios errores, resuélvelos uno por uno
10
Stack Overflow
Primero busca: tu error probablemente ya lo tuvo alguien más. Pregunta solo cuando hayas agotado las demás vías y el problema parezca realmente único
Reflexión de la instructora: hubo un momento en que sintió que "no era programadora de código, sino programadora de bugs". A medida que avances, tus bugs serán más grandes y complejos… y eso también es señal de progreso.
📝 Ejercicio original del curso: retos de depuración#
La lección anuncia "algunos desafíos de codificación que debes depurar" en la lección siguiente. Su contenido no está incluido entre los archivos suministrados. El proyecto del día (Sección 8) propone una actividad equivalente.
En los tutoriales anteriores se documentaron bugs verificados en las soluciones del curso. Tu misión es auditar tus propias versiones de esos proyectos.
Documentar en la bitácora de la Sección 8 los que fallen.
Corregir al menos tres.
Escribir una reflexión: ¿cuál fue el bug más difícil de encontrar? ¿Qué consejo te sirvió más?
12
10. 🏆 Proyecto integrador del Día 13: Kit de Entradas a Prueba de Errores#
Proyecto integrador creado para esta adaptación. Integra funciones con return (Día 10), módulos (Días 4 y 7), while (Día 6), constantes (Día 12) y try/except (Día 13).
Casi todos los bugs de "entrada del usuario" de los últimos días (ValueError, opciones en mayúsculas, números fuera de rango) se repiten en cada proyecto. En lugar de corregirlos una y otra vez, crearás un módulo reutilizable.
Un módulo entradas.py con estas funciones (todas con docstring y return):
Función
Comportamiento
pedir_entero(mensaje, minimo, maximo)
Repite la pregunta hasta recibir un entero dentro del rango; usa try/except ValueError
pedir_decimal(mensaje, minimo, maximo)
Igual, pero con float
pedir_opcion(mensaje, opciones)
Acepta solo una opción de la lista, sin importar mayúsculas (.lower()) ni espacios (.strip())
pedir_si_no(mensaje)
Devuelve True o False; acepta "s", "si", "sí", "n" y "no"
Después:
Refactoriza Adivina el Número (Día 12) para usar pedir_opcion y pedir_entero.
Refactoriza la Calculadora (Día 10) para usar pedir_decimal y pedir_opcion.
Escribe un archivo pruebas_entradas.py que documente, en comentarios, los casos probados para cada función: vacío, texto, decimal, fuera de rango, mayúsculas y espacios.
Hoy la IA puede ser un buen "amigo" del consejo 8: no comparte tus suposiciones. Pero, igual que un compañero, puede equivocarse.
Actividad: la IA como compañera de depuración.
Dale a una IA el Paciente 5 (el descuento) sin decirle cuál es el error. Pregúntale: "¿Por qué este código no aplica el descuento?".
Evalúa: ¿encontró la causa (== en lugar de =)? ¿La explicó con claridad?
Ahora dale el código de mutatecorregido y pregúntale si tiene algún bug. ¿Inventa problemas que no existen? (Las IA a veces "encuentran" errores inexistentes para dar una respuesta.)
Conclusión: ¿en qué se parece y en qué se diferencia pedirle ayuda a una IA y a un compañero?
14
12. 🛠️ Correcciones técnicas al material original#
Archivo
Afirmación en la transcripción
Observación
Tratamiento
094
El primer bug "fue encontrado por Grace Hopper"
La polilla (1947, computador Harvard Mark II) quedó registrada en el cuaderno de trabajo de su equipo. La nota decía "primer caso real de un bug encontrado", porque la palabra bug ya se usaba antes para hablar de fallas
Sección 0
096
Millennials de 1980 a 1994, Generación Z después
Los rangos varían según la fuente (por ejemplo, Pew: 1981–1996)
Nota en el Tema 3
097
try/except con la segunda pregunta dentro del except
Si la segunda entrada también es inválida, el programa se detiene. Solución: while + try
Nota técnica en el Tema 4 y proyecto integrador
099
Módulo propio de matemáticas
No debe llamarse math.py porque taparía el módulo estándar. El del curso es maths.py (no suministrado)
Nota técnica en el Tema 6
100
Retos de depuración en la lección siguiente
No están incluidos entre los archivos suministrados