Fundamentos
El bucle while en Python: repetición con prueba al comienzo
Aprende el bucle while: las cuatro piezas, los patrones de contador, centinela y bandera, las condiciones compuestas y los errores de bucles infinitos.
Por Prof. Rodrigo Faustino · Actualizado el · 14 min de lectura
Métricas de este artículo
- Palabras
- 2747
- Caracteres
- 14.900
- Tiempo de lectura
- ~14 min
El while repite un bloque de código mientras una condición sea verdadera, y la prueba ocurre antes de cada vuelta, incluida la primera. Por eso el bloque puede correr muchas veces, una vez o ninguna. Para que el bucle termine, algo dentro de él necesita cambiar el resultado de la prueba. Cuando eso ocurre, el programa sale del bloque y continúa en la primera línea sin sangría; cuando no ocurre, el bucle corre para siempre.
El artículo anterior mostró por qué existen los bucles y qué preguntas responde todo bucle. Este está dedicado al while: las cuatro piezas que lo forman, los tres patrones que resuelven la mayor parte de los problemas, las condiciones con and y not, el or que genera bucles infinitos, el while dentro de otro y el uso en la ESP32.
Las cuatro piezas
Un while bien escrito tiene cuatro piezas, y la figura las numera en el orden en que Python las ejecuta:
i = 1
while i <= 5:
print("2 x", i, "=", 2 * i)
i += 1
print("fin")La salida es la tabla del 2 hasta el quinto término, de 2 x 1 = 2 a 2 x 5 = 10, y después fin. Las piezas son:
- El valor inicial,
i = 1, que queda antes del bucle y crea la variable que la prueba va a mirar. - La prueba,
i <= 5, que Python evalúa antes de cada vuelta. Si daTrue, entra al bloque; si daFalse, salta a después de él. - El cuerpo, el
print, que es el trabajo repetido. - La actualización,
i += 1, que cambia la variable para que la prueba, en algún momento, déFalse.
La pieza 5 de la figura, el print("fin") sin sangría, no es del bucle. Corre una vez, cuando la prueba falla. Quien aprende el while suele equivocar la posición de una de estas piezas: el valor inicial dentro del bucle, la actualización fuera de él, o la prueba que no mira la variable que cambia.
El bucle que no corre ninguna vez
Como la prueba viene antes del cuerpo, un while cuya condición ya empieza falsa no ejecuta el bloque:
n = 0
while n > 0:
print("nunca aparece")
n = n - 1
print("el bucle no corrió, n vale", n)La salida es solo el bucle no corrió, n vale 0. Esto es una característica útil, no un defecto: un programa que suma los valores escritos y recibe cero de entrada debe sumar nada y responder 0, sin necesidad de un if especial. El while con prueba al comienzo trata ese caso gratis. Existe otro tipo de bucle, que corre el cuerpo antes de probar, y Python no tiene una palabra propia para él. El artículo sobre el "hacer-mientras" muestra cómo simularlo.
Del programa al semestre planificado
- Plan de enseñanza y calendario montados solos
- Diario de clases listo para pegar en el SIGA
- Diapositivas y listas de ejercicios con solucionario
Versión Community gratis · Windows
Tres patrones que resuelven casi todo
La mayoría de los while que se escriben al comienzo caen en uno de estos patrones. Reconocer el patrón ayuda a escribir el bucle sin ensayo y error.
El contador
Cuando se sabe cuántas vueltas dar, una variable cuenta hasta el límite. Es el patrón de la tabla de arriba: valor inicial, prueba contra el límite y += 1 al final del bloque. El contador puede crecer, disminuir (n -= 1) o avanzar de dos en dos (i += 2), y la prueba usa <, <=, > o >= según el sentido. Para este patrón, el for con range() suele ser más corto, y es el tema del próximo artículo de la serie.
El centinela
Cuando es el usuario quien decide cuándo parar, el programa no sabe de antemano cuántas vueltas serán necesarias. Un valor acordado, el centinela, avisa el final. El ejemplo calcula el promedio de notas, y el -1 cierra la lista:
soma = 0
contador = 0
nota = float(input("Nota (-1 para parar): "))
while nota != -1:
soma = soma + nota
contador = contador + 1
nota = float(input("Nota (-1 para parar): "))
if contador > 0:
print("promedio", soma / contador)
else:
print("ninguna nota")Con las entradas 8, 6.5, 9 y -1, la última línea de la salida es promedio 7.8333336. El Python del computador mostraría 7.833333333333333, la misma cuenta con el float de 64 bits (el simulador usa 32 bits, como la placa). Si la primera nota escrita ya es -1, el bucle no corre, el contador queda en 0 y el programa imprime ninguna nota. El if contador > 0 existe por eso: sin él, la división soma / contador sería una división por cero. El desafío Promedio de N usa una variación: el primer número leído dice cuántos valores vendrán después, y el bucle es un contador.
Fíjate en la lectura, que aparece dos veces. La primera, antes del bucle, le da a la prueba el valor con el que comparar. La segunda, en la última línea del bloque, es la actualización del patrón: es lo que cambia en cada vuelta. Quien escribe solo la primera lectura arma un bucle que lee una vez y repite para siempre el mismo valor.
La bandera
A veces el bucle depende de un estado del programa, y no de un número. Una variable booleana, la bandera, guarda ese estado y pasa a False cuando la repetición debe terminar:
carga = 100
ligado = True
while ligado:
carga = carga - 30
print("carga", carga)
if carga <= 20:
ligado = False
print("apagado con", carga)La salida es carga 70, carga 40, carga 10 y apagado con 10. El while ligado: no necesita la comparación, porque ligado ya es un booleano. Quien decide parar es el if dentro del cuerpo, y nota que la vuelta en que la carga llega a 10 termina normalmente: la prueba solo se hace de nuevo al comienzo de la vuelta siguiente. Este patrón aparece en juegos (la partida continúa mientras el jugador tenga vidas), en menús (el menú se repite mientras el usuario no elija salir) y en programas de placa (el aparato funciona mientras no haya error).
Una condición que consume el valor
Una cuarta forma es el bucle en que la propia variable de la prueba se va gastando. La cuenta de dígitos de un número es el ejemplo clásico, y usa la división entera y el resto vistos en el artículo sobre operadores aritméticos:
n = 4821
digitos = 0
soma = 0
while n > 0:
soma = soma + n % 10
n = n // 10
digitos = digitos + 1
print(digitos, soma, n)La salida es 4 15 0. El n % 10 toma el último dígito, y el n // 10 lo descarta. En cada vuelta el número se encoge: 4821, 482, 48, 4, 0. Cuando llega a 0, la prueba n > 0 falla. El programa contó 4 dígitos, sumó 4 + 8 + 2 + 1 = 15 y dejó n en cero, lo que muestra que el bucle destruye el valor original. Si el número necesita usarse después, guarda una copia en una variable antes del bucle.
Condiciones compuestas: and, not y la cuenta de intentos
La condición del while es una expresión booleana como las del if, y acepta and, or y not. Un uso muy común es combinar "todavía hay intentos" con "todavía no acertó", y el ejemplo de la contraseña muestra los dos:
tentativas = 0
acertou = False
while tentativas < 3 and not acertou:
senha = input("Contraseña: ")
tentativas = tentativas + 1
if senha == "1234":
acertou = True
else:
print("contraseña incorrecta")
if acertou:
print("acceso permitido")
else:
print("bloqueado")Con las entradas 1111 y 1234, el programa muestra contraseña incorrecta y después acceso permitido. Si las tres contraseñas son erróneas (1111, 2222 y 3333), muestra contraseña incorrecta tres veces y termina con bloqueado. El bucle tiene dos razones para parar, y cada una es una mitad de la condición: se acabaron los intentos o el usuario acertó. El and exige que las dos sigan verdaderas para repetir. Después del bucle, el if acertou descubre cuál de las dos razones cerró la repetición, un truco que sirve para cualquier bucle con dos salidas. El desafío Contraseña 1234 es la versión más simple de esta idea, sin límite de intentos: repite la lectura hasta la contraseña correcta.
La IA que recuerda tu vida y vive en tu ordenador
- Memoria a largo plazo con la fuente de cada respuesta
- Funciona sin conexión: tus datos se quedan en tu PC
- Lee PDF, diapositivas, fotos y grabaciones de reuniones
Plan Esencial gratis para siempre
El error del or
La pregunta "repite mientras la respuesta no sea s ni n" es una trampa de lógica, y casi todos la escriben mal al menos una vez, porque la frase en español tiene "ni" y el código pide and. La tabla de la figura muestra la cuenta:
for resp in ("s", "n", "x"):
print(resp, resp != "s" or resp != "n", resp != "s" and resp != "n", resp not in ("s", "n"))La salida es s True False False, n True False False y x True True True. La segunda columna, la del or, da True en las tres filas. Quien escribe s pasa la prueba resp != "n", y quien escribe n pasa la prueba resp != "s", así que ninguna respuesta hace que el bucle se detenga, ni siquiera la respuesta correcta. El and y el not in dan False para s y para n, y True solo para la respuesta inválida. La versión correcta, con el mensaje de error y la nueva lectura dentro del bucle, es esta:
resp = input("Continuar (s/n)? ")
while resp != "s" and resp != "n":
print("Responde s o n")
resp = input("Continuar (s/n)? ")
print("respuesta", resp)Con las entradas x, q y s, la salida pide la respuesta de nuevo dos veces y termina con respuesta s. Es la misma situación de las tablas de verdad: cuando la condición parece correcta pero el programa se comporta mal, arma la tabla con los valores posibles. Las leyes de De Morgan, tratadas en ese artículo, explican por qué "no s y no n" es lo mismo que "no (s o n)".
Un while dentro de otro
El cuerpo de un while puede contener otro while. El bucle de adentro corre completo en cada vuelta del de afuera, y el resultado es una cuadrícula:
i = 1
while i <= 3:
j = 1
while j <= 3:
print(i * j, end=" ")
j += 1
print()
i += 1La salida es la tabla de 1 a 3 en tres líneas: 1 2 3, 2 4 6 y 3 6 9. El end=" " cambia el salto de línea del print por un espacio, para que los números queden lado a lado, y el print() vacío, en la sangría del bucle de afuera, corta la línea al final de cada fila. Fíjate en que el j = 1 queda dentro del bucle de afuera: necesita volver a 1 en cada fila. Olvidar esto, o poner el j = 1 antes de los dos bucles, es el defecto clásico, porque en la segunda fila el j ya vale 4 y el bucle de adentro no corre ninguna vez.
Qué cambia en la ESP32
El while de MicroPython es el mismo del Python del computador. Dos usos aparecen todo el tiempo en programas de placa. El primero es el bucle principal, el while True: del main.py, que lee sensores, decide y actúa hasta que se apague la placa. Necesita una pausa con time.sleep o time.sleep_ms dentro del cuerpo, si no corre lo más rápido que el procesador deje. El artículo sobre programar sin colgarse muestra cómo hacer varias tareas dentro de él.
El segundo es esperar algo con límite de tiempo, una espera con timeout. La prueba compara el tiempo que pasó con un límite:
import time
inicio = time.ticks_ms()
voltas = 0
while time.ticks_diff(time.ticks_ms(), inicio) < 200:
voltas = voltas + 1
time.sleep_ms(50)
print("al menos 3 vueltas:", voltas >= 3)La salida es al menos 3 vueltas: True. La función time.ticks_ms() devuelve un contador de milisegundos, y time.ticks_diff calcula la diferencia entre dos valores sin confundirse cuando el contador da la vuelta, lo que el - simple no garantiza, como explica el artículo sobre ticks. El bucle repite mientras hayan pasado menos de 200 ms. El programa imprime solo si hubo al menos tres vueltas, y no el número exacto, porque cambia de una ejecución a otra, y en la placa también depende de la velocidad de la lectura. Es la base de esperas como "aguarda la respuesta del sensor hasta por dos segundos". Un bucle de espera siempre necesita una salida por tiempo, para que la placa no quede atrapada en un sensor que nunca responde.
Los bucles con float piden el cuidado del artículo anterior: comparar con == o != un valor acumulado de 0,1 en 0,1 puede no dar nunca resultado, y una cuenta de vueltas entera como tope es la defensa más simple.
En el aula: dónde suele tropezar el while
Los casos de abajo se repiten en los grupos, y los de bucle infinito son los que más llaman al profesor. En todos el programa corre, y lo que falla es el comportamiento.
La actualización fuera del bucle
La línea de actualización sale de la sangría y la prueba nunca cambia:
i = 1
voltas = 0
while i <= 3 and voltas < 5:
print("i vale", i)
voltas += 1
i = i + 1
print("vueltas", voltas, "i", i)La salida tiene cinco líneas i vale 1, después vueltas 5 i 2. El i = i + 1 está en el margen, después del bucle, así que corre una sola vez, al final. El i queda en 1 durante todo el bucle. Sin el tope del voltas, el programa imprimiría i vale 1 para siempre. El tope es una buena práctica de depuración: un contador de seguridad que corta el bucle en el valor esperado más un margen.
El signo igual en la prueba
El = es asignación y el == es comparación, y un while n = 0: no funciona. Los mensajes varían:
| Error | Simulador (MicroPython) | Python en el computador |
|---|---|---|
while n = 0: | SyntaxError: invalid syntax (línea 2) | SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='? |
while n > 0 sin los dos puntos | SyntaxError: invalid syntax (línea 2) | SyntaxError: expected ':' |
| cuerpo sin sangría | SyntaxError: invalid syntax (línea 3) | IndentationError: expected an indented block after 'while' statement on line 2 |
El simulador dice solo invalid syntax y la línea. La pista es mirar la línea indicada: el = donde debía haber ==, los dos puntos que faltan al final de la línea del while y la línea siguiente que no tiene sangría. El artículo sobre if y else trata los errores de sangría con más detalle.
La segunda lectura que no existe
En el patrón del centinela, quien lee el valor solo antes del bucle se queda con un bucle infinito: la variable de la prueba nunca cambia. Es el mismo error de la actualización olvidada, con una excusa más difícil de ver, porque el input() queda justo encima del while y da la impresión de que la lectura "ya está ahí". La pregunta de prueba es: ¿dónde, dentro del bloque, cambia el valor de nota?
Olvidar el caso de cero vueltas
El promedio sin ninguna nota, la suma de una lista vacía, la contraseña de un usuario que desiste antes de empezar. Si el programa divide por el contador, el if contador > 0 del ejemplo del promedio existe para eso. La prueba que vale la pena hacer en todo while es la primera vuelta ya falsa.
El or en lugar del and
El error de la figura, con la tabla de verdad. La frase en español "mientras no sea s ni n" es un and, y el or produce un bucle que nunca termina, y que pasa sin ningún aviso.
El j que no vuelve al comienzo
En bucles anidados, la variable del bucle de adentro necesita reiniciarse en cada vuelta del de afuera. El síntoma es la segunda fila de la cuadrícula vacía, sin ningún error.
Ejercicio: adivina el número
El programa sortea (aquí, fija) un número secreto y el jugador prueba hasta acertar. En cada intento errado, el programa dice si el secreto es mayor o menor, y al final informa cuántos intentos fueron necesarios. Reúne el centinela (el acierto es el final), un if dentro del bucle y un contador.
segredo = 65
palpite = int(input("Intento: "))
tentativas = 1
while palpite != segredo:
if palpite < segredo:
print("mayor")
else:
print("menor")
palpite = int(input("Intento: "))
tentativas = tentativas + 1
print("acertó en", tentativas, "intentos")Con los intentos 50, 75, 62, 68 y 65, la salida alterna mayor, menor, mayor, menor y termina con acertó en 5 intentos. El contador empieza en 1 porque el primer intento se lee antes del bucle, y cada lectura dentro del bucle suma un intento más. Para ampliar, agrega un límite de intentos a la condición, while palpite != segredo and tentativas < 7, y un mensaje de derrota después del bucle, como en el ejemplo de la contraseña.
Preguntas frecuentes
¿Cuál es la diferencia entre while e if?
El if prueba una vez y sigue adelante. El while vuelve a la prueba después de cada ejecución del bloque y solo sigue cuando falla.
¿Cuándo usar while y cuándo usar for?
Usa for cuando ya se sabe qué recorrer (los números de 1 a 10, los elementos de un grupo). Usa while cuando el final depende de algo que solo aparece corriendo, como una respuesta del usuario, una lectura de sensor o una condición que cambia sola.
¿Qué es while True?
Es un bucle cuya condición nunca queda falsa. Termina por un break dentro del cuerpo o cuando se interrumpe el programa. Es la estructura del bucle principal de la ESP32, y el artículo sobre break y continue muestra los otros usos.
¿El while puede tener else?
Puede, y el bloque del else corre cuando la prueba falla, pero no cuando el bucle se interrumpe por break. El tema es del artículo sobre break, continue y el else de los bucles.
¿Cómo descubro por qué mi bucle no termina?
Pon un print con la variable de la prueba dentro del bucle y sigue los valores, como en la tabla de seguimiento del artículo anterior. Si el valor no cambia, falta la actualización. Si cambia pero nunca llega al límite, el sentido está mal o la condición está mal escrita.
Para continuar
La ruta Lógica con Python tiene los desafíos de repetición. El Hasta cero es el centinela más simple, la Suma dinámica suma valores con el centinela, la Contraseña 1234 repite la lectura hasta acertar, y el Promedio de N combina contador y acumulador. Para la base, relee Por qué repetir.
El próximo tema es el bucle for y la función range(): la forma corta de repetir un número conocido de veces, sin escribir las cuatro piezas a mano.