11. Repetir acciones con bucles while
Los programas a menudo necesitan repetir acciones múltiples veces. Ya has visto cómo tomar decisiones con sentencias if en el Capítulo 8, pero ¿qué pasa si necesitas realizar una acción repetidamente hasta que se cumpla cierta condición? Aquí es donde entran en juego los bucles.
Python ofrece dos tipos principales de bucles: bucles while y bucles for. En este capítulo, nos centraremos en los bucles while, que repiten un bloque de código mientras una condición permanezca verdadera. El bucle for, que es más adecuado para iterar sobre secuencias, se tratará en el Capítulo 12.
Comprender los bucles while es fundamental para escribir programas que puedan procesar datos repetidamente, validar la entrada del usuario, implementar bucles de juego y manejar muchos otros escenarios reales de programación.
11.1) La estructura de un bucle while
Un bucle while ejecuta repetidamente un bloque de código mientras una condición especificada se evalúe como True. Una vez que la condición se convierte en False, el bucle se detiene y el programa continúa con el código después del bucle.
Sintaxis básica de un bucle while
La estructura de un bucle while se ve así:
while condition:
# Bloque de código a repetir
# Este código se ejecuta mientras condition sea TrueLa condition es cualquier expresión que se evalúe como un valor booleano (o que pueda interpretarse como verdadera o falsa, como aprendimos en el Capítulo 7). El bloque de código con sangría debajo de la sentencia while se llama el cuerpo del bucle, y se ejecuta repetidamente mientras la condición permanezca True.
Veamos un ejemplo simple:
# Contar de 1 a 5
count = 1
while count <= 5:
print(f"Count is: {count}")
count = count + 1 # Incrementar count
print("Loop finished!")Output:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Loop finished!Cómo funciona esto paso a paso:
- Inicializamos
counten1 - Se comprueba la condición
count <= 5. Como1 <= 5esTrue, se ejecuta el cuerpo del bucle - Dentro del bucle, imprimimos el contador actual y luego lo incrementamos en 1
- Después de que el cuerpo del bucle termina, Python vuelve a la sentencia
whiley comprueba la condición de nuevo - Este proceso se repite hasta que
countse convierte en6, momento en el cual6 <= 5esFalse, y el bucle termina - El programa continúa con el código después del bucle
La idea clave es que la condición se comprueba antes de cada iteración (cada repetición del cuerpo del bucle). Si la condición es False al inicio, el cuerpo del bucle no se ejecuta en absoluto:
count = 10
while count <= 5:
print("This will never print")
print("Loop skipped entirely")Output:
Loop skipped entirelyComo 10 <= 5 es False desde el inicio, el cuerpo del bucle nunca se ejecuta.
La importancia de modificar la variable del bucle
Para que un bucle while finalmente se detenga, algo dentro del bucle debe cambiar la condición de True a False. Esto normalmente significa modificar la(s) variable(s) utilizada(s) en la condición. Si te olvidas de hacer esto, creas un bucle infinito (del que hablaremos en detalle en la siguiente sección).
Aquí tienes un ejemplo que demuestra por qué es importante actualizar la variable del bucle:
# Calcular la suma de los números del 1 al 10
total = 0
number = 1
while number <= 10:
total = total + number # Sumar el número actual a total
number = number + 1 # Pasar al siguiente número
print(f"The sum of numbers from 1 to 10 is: {total}")Output:
The sum of numbers from 1 to 10 is: 55En este ejemplo, estamos acumulando una suma mientras avanzamos por los números. Tanto total como number cambian en cada iteración, pero es la modificación de number la que asegura que el bucle finalmente termine cuando number se convierte en 11.
Bucles while con entrada del usuario
Un uso práctico de los bucles while es procesar la entrada del usuario hasta que se cumpla una condición específica. Vamos a crear un juego simple de adivinar números:
# Juego simple de adivinar números
secret_number = 7
guess = 0
while guess != secret_number:
guess = int(input("Guess the number (1-10): "))
if guess < secret_number:
print("Too low! Try again.")
elif guess > secret_number:
print("Too high! Try again.")
else:
print("Correct! You guessed it!")Este bucle sigue pidiendo intentos hasta que el usuario introduce el número correcto. Cada iteración procesa un intento y proporciona retroalimentación. El bucle termina de forma natural cuando guess es igual a secret_number, haciendo que la condición guess != secret_number se convierta en False.
Bucles while con múltiples condiciones
Puedes usar operadores booleanos (and, or, not) para crear condiciones de bucle más complejas, como aprendimos en el Capítulo 9:
# Procesar la entrada hasta que el usuario escriba "quit" o alcance 5 intentos
attempts = 0
user_input = ""
while user_input != "quit" and attempts < 5:
user_input = input("Enter a command (or 'quit' to exit): ")
attempts += 1
if user_input == "quit":
print("Goodbye!")
else:
print(f"You entered: {user_input}")
print(f"Attempts remaining: {5 - attempts}")
if attempts >= 5 and user_input != "quit":
print("Maximum attempts reached.")Este bucle continúa mientras ambas condiciones sean verdaderas: el usuario no ha escrito "quit" y no ha superado 5 intentos. El bucle termina cuando cualquiera de las condiciones se vuelve falsa.
Visualizar la ejecución de un bucle while
Aquí tienes un diagrama de flujo que muestra cómo se ejecuta un bucle while:
El bucle crea un ciclo donde se comprueba la condición, el cuerpo se ejecuta si la condición es verdadera, se actualizan variables y luego se comprueba la condición de nuevo. Este ciclo continúa hasta que la condición se vuelve falsa.
11.2) Bucles infinitos y cómo evitarlos
Un bucle infinito es un bucle que nunca termina porque su condición nunca se convierte en False. Los bucles infinitos son uno de los errores más comunes que cometen los principiantes con los bucles while, y pueden hacer que tu programa se quede bloqueado indefinidamente.
¿Qué causa los bucles infinitos?
La causa más común de los bucles infinitos es olvidar modificar la(s) variable(s) que afectan a la condición del bucle. Veamos un ejemplo problemático:
# ADVERTENCIA: Bucle infinito - solo para demostración
# PROBLEMA: count nunca se modifica
count = 1
while count <= 5:
print(f"Count is: {count}")
# Falta: count += 1Si ejecutaras este código, imprimiría "Count is: 1" para siempre porque count permanece en 1, y 1 <= 5 siempre es True. La condición nunca cambia.
Cómo reconocer que esto es un bucle infinito: Mira la condición del bucle (count <= 5) y luego comprueba si algo dentro del cuerpo del bucle modifica count. Si no, y la condición empieza siendo True, tienes un bucle infinito.
Aquí está la versión corregida:
# Versión correcta con incremento adecuado
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # Esto asegura que el bucle finalmente termineOutput:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5Depurar bucles infinitos con límites de seguridad
Al desarrollar código con bucles, es útil añadir un límite de seguridad para evitar bucles infinitos accidentales durante las pruebas:
# Límite de seguridad durante el desarrollo
count = 1
iterations = 0
max_iterations = 100 # Límite de seguridad
while count <= 5 and iterations < max_iterations:
print(f"Count is: {count}")
count += 1
iterations += 1
if iterations >= max_iterations:
print("WARNING: Maximum iterations reached. Check for infinite loop.")Este patrón añade un contador que rastrea cuántas veces se ha ejecutado el bucle. Si alcanza el límite de seguridad, sabes que algo está mal con la lógica de tu bucle. Una vez que estás seguro de que tu bucle funciona correctamente, puedes eliminar el límite de seguridad.
Bucles infinitos con entrada del usuario
Otro escenario común para los bucles infinitos implica la validación de entrada del usuario:
# ADVERTENCIA: Bucle infinito potencial - solo para demostración
# PROBLEMA: Si el usuario nunca introduce una entrada válida, el bucle nunca termina
age = -1
while age < 0:
age = int(input("Enter your age: "))
# Si el usuario introduce un número negativo, el bucle continúaEste bucle funciona correctamente si el usuario finalmente introduce un número no negativo, pero se convierte en un bucle infinito si el usuario sigue introduciendo valores negativos. Aunque esto podría ser aceptable para algunos programas (quieres seguir preguntando hasta recibir una entrada válida), es importante entender que la finalización del bucle depende por completo del comportamiento del usuario.
Un enfoque más robusto podría incluir una forma de salir:
# Mejor enfoque con opción de salida
age = -1
while age < 0:
user_input = input("Enter your age (or 'quit' to exit): ")
if user_input.lower() == 'quit':
print("Exiting program.")
age = 0 # Establecer un valor válido para salir del bucle
else:
age = int(user_input)
if age < 0:
print("Age must be non-negative. Please try again.")
if age > 0:
print(f"Your age is: {age}")Bucles infinitos con errores lógicos
A veces los bucles infinitos ocurren por errores lógicos en la forma en que actualizas las variables:
# ADVERTENCIA: Bucle infinito - solo para demostración
# PROBLEMA: count se decrementa en lugar de incrementarse
count = 1
while count <= 5:
print(f"Count is: {count}")
count -= 1 # ERROR: Esto hace que count sea más pequeño, no más grandeEsto crea un bucle infinito porque count empieza en 1 y se convierte en 0, -1, -2, etc. Como los números negativos siempre son menores o iguales que 5, la condición count <= 5 sigue siendo True para siempre.
La versión corregida:
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # Correcto: incrementar para finalmente superar 5Depurar bucles infinitos
Si creas accidentalmente un bucle infinito mientras ejecutas un script de Python, puedes detenerlo presionando Ctrl+C (o Cmd+C en Mac) en tu terminal. Esto envía una señal de interrupción de teclado a Python, que detiene el programa.
Cómo evitar bucles infinitos:
- Asegúrate siempre de que la condición del bucle pueda convertirse en False: Comprueba que las variables de la condición se modifiquen dentro del bucle
- Usa el operador de comparación correcto: Asegúrate de que
<=,<,!=, etc., coincidan con tu intención - Prueba primero con valores pequeños: Antes de ejecutar un bucle que podría ejecutarse muchas veces, prueba con límites pequeños
- Añade sentencias print de depuración: Imprime temporalmente la variable del bucle para ver cómo cambia:
- Usa límites de seguridad durante el desarrollo: Como se mostró antes, añade un contador máximo de iteraciones mientras pruebas
11.3) Usar break y continue en bucles while
Python proporciona dos sentencias especiales que te dan más control sobre la ejecución de los bucles: break y continue. Estas sentencias te permiten alterar el flujo normal de un bucle en función de condiciones que surgen durante la ejecución.
11.3.1) La sentencia break
La sentencia break termina el bucle inmediatamente, independientemente de la condición del bucle. Cuando Python encuentra break, sale del bucle por completo y continúa con el código después del bucle.
Aquí tienes un ejemplo simple:
# Salir del bucle cuando se alcance un valor específico
count = 1
while count <= 10:
if count == 5:
print("Reached 5, stopping loop")
break
print(f"Count: {count}")
count += 1
print("Loop exited")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Reached 5, stopping loop
Loop exitedObserva que, una vez que count es igual a 5, se ejecuta la sentencia break y el bucle termina inmediatamente. El bucle nunca llega a count = 6 aunque la condición count <= 10 seguiría siendo True.
Cómo break cambia el flujo del bucle
Comprender cómo break altera la ejecución normal del bucle es crucial. Aquí tienes un diagrama de flujo que muestra la diferencia:
El punto clave: break proporciona una ruta de salida inmediata del bucle, omitiendo tanto el resto del código del cuerpo del bucle como la comprobación de la condición.
Uso práctico de break: Validación de entrada
Uno de los usos más comunes de break es salir de un bucle cuando se recibe una entrada válida:
# Seguir pidiendo una entrada válida hasta recibirla
while True:
age_input = input("Enter your age (must be positive): ")
# Intentar convertir a entero
try:
age = int(age_input)
# Comprobar si es válido
if age > 0:
print(f"Thank you! Your age is {age}")
break # Salir del bucle con entrada válida
else:
print("Age must be positive. Please try again.")
except ValueError:
print("That's not a valid number. Please try again.")
print("Input validation complete")Este patrón usa while True: para crear un bucle infinito intencional, y luego usa break para salir cuando se recibe una entrada válida. Esto es más limpio que intentar gestionar una condición de bucle compleja. (Nota: Aquí estamos usando try y except, que aprenderemos en detalle en la Parte VII. Por ahora, solo entiende que captura errores al convertir la entrada a un entero.)
break con múltiples condiciones
Puedes usar break con condiciones complejas para salir de un bucle cuando se cumpla cualquiera de varios criterios:
# Buscar un elemento específico en la entrada del usuario
search_term = "python"
attempts = 0
max_attempts = 5
while attempts < max_attempts:
user_input = input("Enter a word (or 'quit' to exit): ").lower()
attempts += 1
if user_input == 'quit':
print("User requested exit")
break
if user_input == search_term:
print(f"Found '{search_term}'!")
break
print(f"'{user_input}' is not '{search_term}'. Try again.")
print(f"Attempts remaining: {max_attempts - attempts}")
if attempts >= max_attempts:
print("Maximum attempts reached")Este bucle puede salir de tres formas:
- El usuario introduce "quit" (primer
break) - El usuario introduce el término de búsqueda (segundo
break) - Se alcanza el máximo de intentos (la condición del bucle se convierte en
False)
11.3.2) La sentencia continue
La sentencia continue omite el resto de la iteración actual y vuelve a la comprobación de la condición del bucle. A diferencia de break, que sale del bucle por completo, continue solo salta a la siguiente iteración.
Aquí tienes un ejemplo básico:
# Imprimir solo los números impares del 1 al 10
count = 0
while count < 10:
count += 1
if count % 2 == 0: # Si es un número par
continue # Omitir el resto e ir a la siguiente iteración
print(f"Odd number: {count}")Output:
Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9Cómo funciona esto:
countse incrementa al inicio de cada iteración- Si
countes par (count % 2 == 0), se ejecutacontinue - La sentencia
continueomite la sentenciaprinty vuelve a la condición delwhile - Si
countes impar,continueno se ejecuta, así que se ejecuta la sentenciaprint
Cómo continue cambia el flujo del bucle
Aquí tienes un diagrama de flujo que muestra cómo continue afecta la ejecución del bucle:
La diferencia crucial entre break y continue:
- break: Sale del bucle completamente, saltando al código después del bucle
- continue: Omite el código restante en la iteración actual, vuelve a la comprobación de la condición
Nota importante sobre la ubicación de continue
Observa que count += 1 viene antes del continue. Si lo colocáramos después, los números pares harían que continue omitiera el incremento, creando un bucle infinito:
# ADVERTENCIA: Bucle infinito - solo para demostración
# PROBLEMA: continue omite el incremento para números pares
count = 0
while count < 10:
if count % 2 == 0:
continue # Omite todo lo de abajo, incluyendo count += 1
count += 1 # ERROR: Esto nunca se ejecuta para números pares
print(f"Odd number: {count}")Cuando count es 0 (par), se ejecuta continue, omitiendo count += 1. El bucle vuelve a comprobar 0 < 10, y el ciclo se repite para siempre.
La regla: Asegúrate siempre de que las variables del bucle que afectan a la condición se actualicen antes de cualquier sentencia continue que pueda omitir el resto del cuerpo del bucle.
Uso práctico de continue: Filtrar datos
La sentencia continue es útil cuando estás procesando datos y quieres omitir ciertos elementos:
# Procesar solo puntuaciones válidas
score_count = 0
total_score = 0
attempts = 0
while attempts < 5:
score_input = input(f"Enter score {attempts + 1} (or 'skip' to skip): ")
attempts += 1
if score_input.lower() == 'skip':
print("Skipping this score")
continue # Saltar a la siguiente iteración
try:
score = int(score_input)
if score < 0 or score > 100:
print("Score must be between 0 and 100. Skipping.")
continue # Omitir puntuaciones no válidas
# Puntuación válida: procesarla
total_score += score
score_count += 1
print(f"Score recorded: {score}")
except ValueError:
print("Invalid input. Skipping.")
continue
if score_count > 0:
average = total_score / score_count
print(f"\nAverage of {score_count} valid scores: {average:.1f}")
else:
print("\nNo valid scores entered")Este ejemplo demuestra múltiples usos de continue:
- Omitir cuando el usuario introduce "skip"
- Omitir cuando la puntuación está fuera del rango válido
- Omitir cuando la entrada no es un número válido
Cada continue evita que la puntuación se añada al total, pero el bucle continúa con el siguiente intento.
Combinar break y continue
Puedes usar tanto break como continue en el mismo bucle para un control sofisticado:
# Procesar números hasta que la suma supere 100, omitiendo números negativos
total = 0
count = 0
while True:
number_input = input("Enter a number (or 'done' to finish): ")
if number_input.lower() == 'done':
print("User finished entering numbers")
break # Salir del bucle
try:
number = int(number_input)
if number < 0:
print("Negative numbers not allowed. Skipping.")
continue # Saltar a la siguiente iteración
total += number
count += 1
print(f"Added {number}. Current total: {total}")
if total > 100:
print("Total exceeded 100. Stopping.")
break # Salir del bucle cuando se alcance el límite
except ValueError:
print("Invalid input. Skipping.")
continue
print(f"\nFinal total: {total} (from {count} numbers)")Este bucle demuestra:
breakpara salir cuando el usuario escribe "done"continuepara omitir números negativoscontinuepara omitir entradas no válidasbreakpara salir cuando el total supera 100
11.4) Usar else con bucles while
Python tiene una característica única que muchos otros lenguajes de programación no tienen: puedes adjuntar una cláusula else a un bucle while. Este bloque else se ejecuta solo si el bucle termina normalmente (es decir, la condición del bucle se convierte en False sin encontrarse con una sentencia break).
Sintaxis básica de else con while
La sintaxis se ve así:
while condition:
# Cuerpo del bucle
else:
# Esto se ejecuta solo si el bucle termina normalmente
# (no interrumpido por break)Veamos un ejemplo simple:
# Contar de 1 a 5 con cláusula else
count = 1
while count <= 5:
print(f"Count: {count}")
count += 1
else:
print("Loop completed normally")
print("Program continues")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Loop completed normally
Program continuesEl bloque else se ejecuta porque el bucle se ejecutó hasta que su condición (count <= 5) se convirtió en False. El bucle terminó "normalmente" sin ser interrumpido.
Cuándo else NO se ejecuta: La sentencia break
El comportamiento clave de la cláusula else es que no se ejecuta si el bucle termina por una sentencia break:
# Buscar un número con break
count = 1
target = 3
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Found 3!
Search completeObserva que el bloque else ("Target not found in range") no se ejecutó porque el bucle terminó por break. Esta es la distinción crucial: else se ejecuta solo cuando el bucle sale normalmente (la condición se convierte en False), no cuando sale mediante break.
Ahora veamos qué pasa cuando no se encuentra el objetivo:
# Buscar un número que no existe
count = 1
target = 7 # Not in range 1-5
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Checking: 4
Checking: 5
Target not found in range
Search completeEsta vez, el bucle completó todas las iteraciones sin encontrar el objetivo, así que la condición count <= 5 finalmente se convirtió en False, y se ejecutó el bloque else.
Cómo funciona else con la finalización del bucle
Aquí tienes un diagrama de flujo que muestra las rutas de ejecución con la cláusula else:
El bloque else solo se alcanza cuando la condición del bucle se convierte en False de forma natural. Si se encuentra break, el flujo salta directamente por encima del bloque else hacia el código después del bucle.
Uso práctico: Operaciones de búsqueda
La cláusula else es particularmente útil para operaciones de búsqueda donde quieres saber si se encontró algo:
# Buscar una contraseña válida en una lista de intentos
valid_password = "python123"
max_attempts = 3
attempts = 0
while attempts < max_attempts:
password = input(f"Enter password (attempt {attempts + 1}/{max_attempts}): ")
attempts += 1
if password == valid_password:
print("Access granted!")
break
else:
print("Access denied. Maximum attempts exceeded.")
print("Account locked.")Si el usuario introduce la contraseña correcta, se ejecuta break y se omite el bloque else. Si se usan todos los intentos sin éxito, el bucle termina normalmente y se ejecuta el bloque else, indicando el fallo.
else con continue
La sentencia continue no evita que se ejecute el bloque else. Solo break hace eso:
# continue no afecta la ejecución de else
count = 0
while count < 5:
count += 1
if count == 3:
print(f"Skipping {count}")
continue # Saltar a la siguiente iteración
print(f"Processing {count}")
else:
print("Loop completed normally (continue doesn't prevent this)")Output:
Processing 1
Processing 2
Skipping 3
Processing 4
Processing 5
Loop completed normally (continue doesn't prevent this)El bloque else se ejecuta porque el bucle terminó normalmente. La sentencia continue solo afecta a iteraciones individuales, no a la finalización global del bucle.
Comparar else con y sin break
Veamos una comparación lado a lado:
# Ejemplo 1: Encontrar el primer número divisible por 7 (con break)
print("Finding first number divisible by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
break
number += 1
else:
print("No number divisible by 7 found in range")
print()
# Ejemplo 2: Comprobar todos los números (sin break)
print("Checking all numbers for divisibility by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
number += 1
else:
print("Finished checking all numbers")Output:
Finding first number divisible by 7:
Found: 7
Checking all numbers for divisibility by 7:
Found: 7
Found: 14
Finished checking all numbersEn el primer ejemplo, break detiene el bucle después de encontrar la primera coincidencia, así que else no se ejecuta. En el segundo ejemplo, el bucle comprueba todos los números y termina normalmente, así que else se ejecuta.
Cuándo usar else con bucles while
La cláusula else es más útil cuando:
- Operaciones de búsqueda: Quieres saber si se encontró algo o no
- Validación con intentos limitados: Necesitas manejar el caso en el que se agotan todos los intentos
- Procesamiento con salida temprana: Quieres un comportamiento distinto para "completó todo" vs "se detuvo antes"
Sin embargo, else con bucles puede ser confuso para programadores que vienen de otros lenguajes (donde esta característica no existe). A veces es más claro usar una variable bandera:
# Usar la cláusula else
attempts = 0
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
break
else:
print("Access denied")
# Equivalente usando una variable bandera (a veces más claro)
attempts = 0
access_granted = False
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
access_granted = True
break
if not access_granted:
print("Access denied")Ambos enfoques funcionan. Elige el que haga tu código más claro para tu situación específica.
Comprender else con bucles while te da otra herramienta para escribir código claro y expresivo, especialmente en escenarios de búsqueda y validación donde necesitas distinguir entre "encontrado" y "no encontrado" o "éxito" y "fallo después de todos los intentos".
En este capítulo, hemos explorado los bucles while en profundidad, aprendiendo cómo:
- Estructurar bucles que repiten código según condiciones
- Evitar bucles infinitos actualizando correctamente las variables del bucle
- Usar
breakpara salir de los bucles antes de tiempo cuando sea necesario - Usar
continuepara omitir iteraciones según condiciones - Usar
elsepara manejar casos en los que los bucles terminan normalmente sinbreak
Estas herramientas te dan un control potente sobre la repetición en tus programas. En el próximo capítulo, aprenderemos sobre los bucles for, que ofrecen una forma más conveniente de iterar sobre secuencias como cadenas, listas y rangos.