Un bucle infinito ocurre cuando una repetición nunca alcanza su condición de salida. A veces es intencional, como en un servidor que espera solicitudes, pero en programas normales suele manifestarse como una terminal que no responde, consumo elevado de CPU o una interfaz congelada. Corregirlo exige identificar qué variable controla el ciclo, cómo cambia y qué situaciones permiten terminar.
Para comprender mejor el problema, revisa los artículos sobre bucles while, bucles for, manejo de excepciones, depuración con pdb y logging en Python.
Las referencias externas principales son la documentación oficial de la sentencia while y la documentación oficial del depurador pdb.
El ejemplo más simple
contador = 0
while contador < 5:
print(contador)
El valor de contador nunca cambia, por lo que la condición siempre permanece verdadera. La corrección es actualizar la variable:
contador = 0
while contador < 5:
print(contador)
contador += 1
Al revisar un bucle, pregunta siempre qué valores aparecen en la condición y en qué línea se modifican.
Condición imposible de alcanzar
numero = 10
while numero != 0:
numero += 1
El número se aleja de cero. La dirección del cambio debe ser compatible con la condición:
numero = 10
while numero != 0:
numero -= 1
También debes considerar que los datos pueden comenzar en un rango inesperado. Si numero fuera negativo, restar seguiría alejándolo del objetivo.
Usar límites más seguros
Cuando buscas un umbral, una comparación puede ser más robusta que esperar igualdad exacta:
numero = 10
while numero > 0:
numero -= 1
La condición expresa la intención real: repetir mientras el número sea positivo.
Errores con números flotantes
valor = 0.0
while valor != 1.0:
valor += 0.1
Los floats no representan exactamente todos los decimales, por lo que el valor puede no ser exactamente 1.0. Utiliza un límite:
valor = 0.0
while valor < 1.0:
valor += 0.1
Para cálculos sensibles, utiliza math.isclose o Decimal según el problema.
Entrada del usuario que nunca cambia
opcion = input("Escribe salir: ")
while opcion != "salir":
print("Opción inválida")
La lectura está fuera del bucle. Debe repetirse:
while True:
opcion = input("Escribe salir: ").strip().lower()
if opcion == "salir":
break
print("Opción inválida")
Normalizar espacios y mayúsculas evita que una entrada aparentemente correcta falle.
Olvidar break
while True:
comando = input("Comando: ").strip()
if comando == "salir":
print("Cerrando")
El programa reconoce la orden, pero no termina. Añade break:
while True:
comando = input("Comando: ").strip()
if comando == "salir":
print("Cerrando")
break
continue antes de actualizar el estado
numero = 0
while numero < 10:
if numero % 2 == 0:
continue
numero += 1
En la primera iteración, continue vuelve al inicio antes del incremento. Actualiza el estado antes de continuar:
numero = 0
while numero < 10:
numero += 1
if numero % 2 == 0:
continue
print(numero)
Otra opción es reorganizar la condición para evitar continue.
Indentación incorrecta
contador = 0
while contador < 5:
print(contador)
# El incremento quedó fuera
contador += 1
Python utiliza la indentación para definir el cuerpo del bucle. Revisa visualmente el bloque y configura el editor para mostrar espacios y niveles.
Modificar la variable equivocada
intentos = 0
intento = 0
while intentos < 3:
intento += 1
Los nombres parecidos ocultan el error. Utiliza nombres claros y revisa exactamente cuál aparece en la condición:
intentos_realizados = 0
while intentos_realizados < 3:
intentos_realizados += 1
Bucle infinito al recorrer una colección
tareas = ["a", "b", "c"]
while tareas:
print(tareas[0])
La lista nunca pierde elementos. Si la intención es consumirlos:
while tareas:
tarea = tareas.pop(0)
print(tarea)
Sin embargo, pop(0) en una lista es costoso para muchos elementos. Una deque con popleft() es mejor para una cola.
Añadir elementos mientras se procesa
pendientes = [1, 2, 3]
while pendientes:
valor = pendientes.pop()
pendientes.append(valor)
Cada elemento retirado vuelve inmediatamente. Define cuándo un dato debe regresar a la cola y establece un máximo de reintentos.
pendientes = [(1, 0), (2, 0), (3, 0)]
max_reintentos = 3
while pendientes:
valor, intentos = pendientes.pop()
procesado = valor % 2 == 0
if not procesado and intentos < max_reintentos:
pendientes.append((valor, intentos + 1))
Esperas activas que consumen CPU
while not archivo_listo():
pass
Aunque la condición pueda terminar, el bucle consume CPU continuamente. Añade una pausa o usa mecanismos de eventos:
import time
while not archivo_listo():
time.sleep(0.5)
En aplicaciones concurrentes, threading.Event, colas o callbacks suelen ser más adecuados que consultar sin descanso.
Añadir un límite defensivo
max_iteraciones = 1000
for iteracion in range(max_iteraciones):
if condicion_de_salida():
break
else:
raise RuntimeError("No se alcanzó la condición de salida")
Un límite convierte un bloqueo silencioso en un error detectable. Es especialmente útil en algoritmos iterativos, reintentos de red y simulaciones.
Usar un timeout
import time
inicio = time.monotonic()
timeout = 10
while True:
if condicion_de_salida():
break
if time.monotonic() - inicio >= timeout:
raise TimeoutError("La operación tardó demasiado")
time.sleep(0.1)
time.monotonic() es apropiado para medir duración porque no retrocede si cambia el reloj del sistema.
Depurar con prints temporales
while condicion:
print({
"contador": contador,
"condicion": condicion,
})
contador += 1
Mostrar el estado puede revelar que una variable permanece igual. Elimina estos prints después o reemplázalos por logging con un nivel adecuado. Nunca imprimas secretos.
Depurar con pdb
import pdb
contador = 0
while contador < 5:
pdb.set_trace()
contador += 1
Dentro del depurador puedes inspeccionar variables, avanzar línea por línea y confirmar si se ejecuta la actualización. En Python moderno también puedes usar breakpoint().
Detener un programa bloqueado
En una terminal, Ctrl+C normalmente envía una interrupción que produce KeyboardInterrupt. Puedes usarla durante el desarrollo:
try:
while True:
ejecutar_tarea()
except KeyboardInterrupt:
print("Proceso detenido por el usuario")
No captures KeyboardInterrupt para ignorarlo y continuar indefinidamente. Utilízalo para limpiar recursos y terminar.
Bucles infinitos intencionales
Servidores, workers y aplicaciones gráficas pueden mantener un ciclo permanente. Aun así, necesitan una forma de apagado:
from threading import Event
parar = Event()
while not parar.is_set():
procesar_siguiente_tarea()
parar.wait(1)
El evento permite detener el ciclo sin esperar una pausa larga. También facilita pruebas y apagados ordenados.
Pruebas que detectan el problema
Separa la lógica de la repetición y prueba que el estado progresa:
def siguiente(valor):
return valor - 1
def test_siguiente_se_acerca_a_cero():
assert siguiente(5) < 5
Para funciones potencialmente largas, ejecuta pruebas con límites pequeños y timeouts proporcionados por el entorno de testing.
Lista de comprobación
Cuando un bucle no termina, revisa la condición, identifica todas sus variables, confirma que cambian en cada ruta, busca continue antes de las actualizaciones, verifica la indentación y analiza valores límite. Después añade un máximo de iteraciones, timeout o señal de cancelación cuando corresponda.
Errores frecuentes
Los errores más comunes son olvidar incrementar un contador, modificar otra variable, esperar igualdad exacta con floats, no volver a leer la entrada, añadir de nuevo cada elemento consumido y utilizar while True sin break. También es frecuente confundir un proceso lento con un bucle infinito; el logging y el perfilado ayudan a distinguirlos.
Conclusión
Un bucle infinito se corrige entendiendo cómo evoluciona su estado. La condición debe poder volverse falsa o existir una salida explícita. Usa nombres claros, límites defensivos, timeouts y herramientas de depuración. Incluso los bucles permanentes intencionales necesitan cancelación y apagado ordenado para ser confiables.






