El bucle while de Python hace que se ejecute un bloque de código re­pe­ti­da­me­n­te mientras una condición sea verdadera. En Python, los bucles while se utilizan pri­n­ci­pa­l­me­n­te cuando el número de ite­ra­cio­nes ne­ce­sa­rias no viene de­te­r­mi­na­do de antemano. A co­n­ti­nua­ción, descubre cómo funciona el bucle while en Python.

Consejo

Aprende a escribir código Python por tu cuenta con nuestro tutorial de Python.

¿Qué es el bucle while en Python?

El bucle while en Python es una es­tru­c­tu­ra de control. Las es­tru­c­tu­ras de control de­te­r­mi­nan qué ruta de código se sigue en el momento de la ejecución. Los bucles se utilizan ge­ne­ra­l­me­n­te para ejecutar re­pe­ti­da­me­n­te un mismo bloque de código. A co­n­ti­nua­ción, te mostramos un resumen de las es­tru­c­tu­ras de control más im­po­r­ta­n­tes en Python:

Es­tru­c­tu­ra de control en Python Ex­pli­ca­ción
Bi­fu­r­ca­ción if else Ejecuta el bloque de código una vez si la condición es verdadera.
Bucle while de Python Ejecuta el bloque de código re­pe­ti­da­me­n­te mientras la condición sea verdadera.
Bucle For de Python Itera sobre los elementos de una colección, eje­cu­ta­n­do un bloque de código para cada elemento.

Los pro­gra­ma­do­res menos ex­pe­ri­me­n­ta­dos no suelen conocer los bucles e intentan emular su fu­n­cio­na­mie­n­to. En lugar de definir una es­tru­c­tu­ra de datos con varios elementos y pro­ce­sar­la con un bucle, definen una variable in­de­pe­n­die­n­te para cada conjunto de datos. Esto supone que se duplique el código para procesar los conjuntos de datos y que se obtenga como resultado un código in­e­fi­cie­n­te. A co­n­ti­nua­ción, se muestra un ejemplo en el que se han utilizado tres personas con sus nombres y edades:

person1 = ('Jim', 17)
person2 = ('Jack', 42)
person3 = ('John', 63)
print(f"{person1[0]}, {person1[1]} years old")
print(f"{person2[0]}, {person2[1]} years old")
print(f"{person3[0]}, {person3[1]} years old")

Además de la in­de­sea­ble du­pli­ca­ción de código, este enfoque plantea un problema logístico: si el número de conjuntos de datos no se conoce hasta el momento de la ejecución, las variables in­di­vi­dua­les co­rre­s­po­n­die­n­tes no pueden definirse de antemano. Para resolver este problema, se utilizan co­le­c­cio­nes de elementos y bucles para procesar esas co­le­c­cio­nes.

Si se sabe cuántas re­pe­ti­cio­nes se necesitan en el momento de la ejecución, el bucle for de Python ofrece el mejor resultado. Como ejemplo, se redacta el código de tal manera que muestre los nombres y edades de tres personas uti­li­za­n­do un bucle for. El código funciona sin du­pli­ci­da­des y contiene solo dos variables, in­de­pe­n­die­n­te­me­n­te del número de conjuntos de datos:

people = ('Jim', 'Jack', 'John')
ages = (17, 42, 63)
for person, age in zip(people, ages):
    print(f"{person}, {age} years old")

A di­fe­re­n­cia del bucle for, el bucle while se utiliza en Python cuando no se sabe cuántas ite­ra­cio­nes van a hacer falta. Un ejemplo claro es el in­te­r­ca­m­bio de mensajes a través de una conexión abierta. Mientras la conexión esté abierta, los mensajes se procesan. Una sentencia if en el cuerpo del bucle evalúa una señal y termina la conexión si así desea:

while connection_open():
    print('Ready to receive')
    process_messages()
    if should_close_connection():
        close_connection()
# once loop terminates
print('Connection was closed')

Además, los bucles while se utilizan para efectuar un número de re­pe­ti­cio­nes bá­si­ca­me­n­te ilimitado. Ejemplos conocidos son los cajeros au­to­má­ti­cos, el prompt de Linux y el “Read Eval Print Loop” (REPL) de Python. A co­n­ti­nua­ción, te mostramos una re­pre­se­n­ta­ción es­que­má­ti­ca de una im­ple­me­n­ta­ción de REPL a través de un bucle while infinito:

# Loop
while True:
    # Read user input
    user_input = read_input()
    # Evaluate input and produce result
    result = eval(user_input)
    # Print result
    print(result)

¿Cómo funciona el bucle while en Python?

El bucle while de Python funciona de forma similar a la bi­fu­r­ca­ción if else de Python. Ambas es­tru­c­tu­ras de control constan de dos partes:

  1. una condición de ser evaluada
  2. un cuerpo con di­re­c­tri­ces

La di­fe­re­n­cia entre ambos es la fre­cue­n­cia con la que se ejecuta el cuerpo. El cuerpo de una sentencia if se ejecuta una única vez:

if condition:
    run_once()

A di­fe­re­n­cia de la sentencia if, el cuerpo del bucle while en Python se ejecuta varias veces si es necesario:

while condition:
    run_again()

Se presenta el patrón general al ejecutar un bucle while de Python:

  1. Se evalúa la condición.
  2. Si la condición es verdadera, se ejecuta el cuerpo del bucle.
  3. La condición se evalúa de nuevo:
    1. Si la condición se sigue cu­m­plie­n­do, se repite este proceso.
    2. Si la condición es falsa, el bucle termina.

Al igual que la sentencia if, un bucle while en Python puede tener un bloque else opcional. El bloque else se ejecuta una vez si la condición es o se convierte en falsa:

while False:
    # this code doesn't loop
    never_runs()
else:
    # instead, this code runs once
    runs_once()

¿En qué se di­fe­re­n­cian los bucles for y while en Python?

El bucle while también tiene relación con el bucle for en Python. Ambos ejecutan un mismo bloque de código re­pe­ti­da­me­n­te, proceso también conocido como “iterar”. La principal di­fe­re­n­cia radica en el número de ite­ra­cio­nes que efectúa cada uno.

En Python, los bucles for se utilizan pri­n­ci­pa­l­me­n­te para iterar sobre los elementos de una colección. El número máximo de ite­ra­cio­nes viene limitado por la longitud de la colección. A co­n­ti­nua­ción, se itera sobre las letras de la palabra “Python” y se emite cada letra in­di­vi­dua­l­me­n­te:

for letter in 'Python':
    print(letter)

El bucle while de Python no se es­pe­cia­li­za en iterar co­le­c­cio­nes y puede uti­li­zar­se de manera flexible. En general, el bucle while sienta la base del bucle. La utilidad que ofrece un bucle for también puede co­n­se­gui­r­se mediante un bucle while, pero no al revés.

A co­n­ti­nua­ción, algunos ejemplos. Se imita la función de un bucle for co­n­ve­n­cio­nal con una variable de bucle numérico, como es el caso del bucle while de Python. Para ello, se define una variable contadora fuera del bucle y se aumenta su valor dentro del cuerpo de este:

counter = 0
limit = 10
while counter < limit:
    print(counter)
    counter += 1

El bucle for equi­va­le­n­te es más corto y directo:

for counter in range(10):
    print(counter)

Si se utiliza un bucle while para iterar sobre las letras de una palabra, este se comporta de la misma manera. Se usa un iterador y la función next(). Si el iterador se agota, se devuelve None en lugar de una letra y, a su misma vez, el bucle termina. El código re­su­l­ta­n­te es mucho más complejo que el bucle for equi­va­le­n­te. Queda de­mo­s­tra­do que el bucle while en Python no es la he­rra­mie­n­ta óptima para resolver este problema:

word = 'Python'
letters = iter(word)
letter = ''
while letter is not None:
    letter = next(letters, None)
    if letter:
        print(letter)

Cuidado con los bucles infinitos de while en Python

En pa­r­ti­cu­lar, los bucles while en Python son buenos im­ple­me­n­ta­n­do bucles infinitos. Esto puede parecer absurdo en un principio. No obstante, los bucles infinitos pro­du­ci­dos por un error son muy temidos, ya que la condición nunca deja de ser cierta, lo que provoca que el programa se cuelgue:

while True:
    print("Forever…")

De hecho, los bucles infinitos in­te­n­cio­na­dos tienen un gran número de usos. Un bucle infinito while producido por error suele ser provocado por una expresión que siempre se evalúa como True. He aquí un ejemplo:

while 1 == 1 + 1 - 1:
    print("And ever…")
Consejo

Si te en­cue­n­tras atrapado en un bucle while infinito de REPL en Python, la co­m­bi­na­ción de teclas Ctrl + C o Strg + C te sacará del apuro. Esta co­m­bi­na­ción envía una señal de parada al in­té­r­pre­te de Python, que cancela la ejecución del bucle.

Abortar y evitar que se ejecute un bucle while en Python

Ge­ne­ra­l­me­n­te, un bucle while sigue iterando hasta que la condición del bucle deja de cumplirse. Un truco común es utilizar una variable “Flag” como condición. Para ello, se define una variable booleana fuera del bucle y se evalúa en la condición del bucle. Cuando se alcanza una de­te­r­mi­na­da condición dentro del cuerpo del bucle se activa el Flag. Cuando la condición se evalúe pre­via­me­n­te a la siguiente ejecución, el nuevo valor hará que el bucle finalice:

aborted = False
while not aborted:
    print("Still going…")
    if some_cond:
        # will prevent next iteration
        aborted = True

Este patrón se encuentra muy a menudo, pero no es es­pe­cia­l­me­n­te elegante. ¿Qué pasa si hay más código en el cuerpo del bucle después de que se haya activado la variable Flag? Este debería ser omitido. De forma práctica, Python conoce la sentencia break para los bucles while.

Si se ejecuta una sentencia break dentro de un bucle, este finaliza in­me­dia­ta­me­n­te. Así pues, la sentencia break de los bucles es similar a la sentencia return de las funciones. Sin embargo, break no devuelve un valor. Es habitual utilizar la sentencia break para poner fin a un bucle infinito:

while True:
    print("Still going…")
    if some_cond:
        break
        # we never get here
        print("We shouldn't be here")
# we end up here after breaking
print("Done.")

La sentencia continue se asemeja co­n­ce­p­tua­l­me­n­te a la sentencia break. Si se ejecuta una sentencia continue en el cuerpo del bucle, se omite el código que le sigue y comienza una nueva iteración. Con break y continue se pueden im­ple­me­n­tar menús sencillos basados en texto, como era habitual en los primeros juegos de ordenador:

# `continue` takes us here
while True:
    print("Press G to start a new game")
    print("Press S to see stats")
    print("Press M for main menu")
    print("Press Q to quit")
    
    key_press = input("Your choice \n")[0].upper()
    print(f"You pressed {key_press}")
    
    if key_press == "G":
        # start game routines
        print("Starting game …")
    elif key_press == "S":
        # show stats
        print("Showing stats …")
    elif key_press == "M":
        # back to main menu
        print("Returning to menu")
        continue
    elif key_press == "Q":
        # break out of loop
        print("Quitting")
        break
    else:
        print("Unknown command. Try again")
# `break` takes us here
...

Salir de bucles while de Python anidados entre sí

El uso de bucles anidados puede generar rá­pi­da­me­n­te confusión. Es por eso por lo que es útil in­te­rru­m­pir el último bucle iniciado por medio de una sentencia break. ¿Pero qué pasa si también se pretende abandonar un bucle superior de la cadena en la misma línea? No hay una palabra clave para este caso concreto; en principio, existe una solución con una variable flag.

Una forma más elegante de salir de los bucles while anidados en Python es combinar break, continue y else con un poco de habilidad. Se construye una cadena de bucles de manera que el bucle exterior termine cuando se ejecute una sentencia break en el bucle interior. Para ello, se utiliza una sentencia continue dentro del bloque else interno para omitir el break externo si es necesario:

# `continue` takes us here
while outer_cond:
    while inner_cond:
        ...
        if some_cond:
            print("Breaking out of inner loop")
            break
    # no inner `break` occured
    else:
        print("Continuing outer loop")
        # skip rest of outer loop body
        continue
    # we only get here if inner `break` occured
    print("Breaking out of outer loop")
    break
# outer `break` takes us here
...

¿Cómo utilizar el bucle while en Python?

En la práctica, el bucle while en Python tiene una gran cantidad de usos di­fe­re­n­tes. Ge­ne­ra­l­me­n­te, los bucles while se utilizan para al­go­ri­t­mos en los que el número de re­pe­ti­cio­nes no está fijado de antemano o cambia durante su ejecución. Los bucles while se utilizan a menudo en co­m­bi­na­ción con otras es­tru­c­tu­ras de control, como las bi­fu­r­ca­cio­nes y las se­n­te­n­cias try-else. He aquí algunos ejemplos.

Consumir una colección en Python con el bucle while

Python ofrece el bucle for para iterar sobre los elementos de una colección. Al menos es así mientras no se cambie la colección desde el cuerpo interno del bucle. ¿Pero qué pasa si se realizan cambios a medida que se itera por los elementos? Es posible imaginar que se quieren eliminar elementos de la colección mientras se itera. En este caso, se dice que la colección se “consume”.

Los bucles for pueden causar errores extraños si la colección su­b­ya­ce­n­te cambia durante la iteración. El bucle while es el más adecuado para consumir una colección en Python. Se utiliza la colección di­re­c­ta­me­n­te como una condición del bucle while. Mientras la colección contenga elementos, se evaluará como verdadera en el contexto booleano. Si la colección se encuentra vacía, el bucle finaliza:

pieces = ['x', 'o', 'o', 'o', 'x', 'o', 'x', 'x']
while pieces:
    piece = pieces.pop()
    print(f"Removed {piece}")
# test
assert pieces == []

Im­ple­me­n­tar la propia función range() con el bucle while en Python

Los bucles while en Python pueden uti­li­zar­se para im­ple­me­n­tar los llamados ge­ne­ra­to­rs. Un generator es una función que utiliza la sentencia yield y genera valores bajo demanda. Se escribe la im­ple­me­n­ta­ción propia de la función range(). Se utiliza la sentencia yield dentro de un bucle while para generar números continuos. Cuando el código llega a la sentencia yield, esta emite un valor y el bucle se detiene:

def my_range(start, stop):
    # only positive ranges implemented
    if stop <= start:
        return None
    current = start
    while current < stop:
        yield current
        # next call of next() continues here
        current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))

Optimizar un modelo con el bucle while de Python

La op­ti­mi­za­ción de modelos forma parte del re­pe­r­to­rio habitual de las di­s­ci­pli­nas cie­n­tí­fi­cas. Un modelo se calcula a partir de un conjunto de pa­rá­me­tros. A co­n­ti­nua­ción, se ajustan los pa­rá­me­tros y se vuelve a calcular el modelo. Se utiliza una función objetivo para estimar si al mo­di­fi­car­se los pa­rá­me­tros se obtiene un mejor modelo. Si este fuera el caso, se repite el proceso. De este modo, se pueden encontrar los pa­rá­me­tros óptimos para el modelo de forma iterativa.

No­r­ma­l­me­n­te, el modelo se ajusta tras unas cuantas ite­ra­cio­nes, de modo que cada vez se consigue una mayor precisión. Si el progreso cae por debajo de un de­te­r­mi­na­do umbral, se detiene la op­ti­mi­za­ción. Para ga­ra­n­ti­zar que el bucle termine, también se limita el número máximo de eje­cu­cio­nes pe­r­mi­ti­das. He aquí un enfoque es­que­má­ti­co de la op­ti­mi­za­ción del modelo uti­li­za­n­do un bucle while de Python:

limit = 5
round = 0
progress = True
while progress and round < limit:
    # attempt next optimization
    round += 1
    # compute optimized parameters
    params = optimize(params)
    # make a copy of the old model
    old_model = model
    # compute new model using optimized parameters
    model = run(model, params)
    # worthwhile to further optimize?
    progress = has_improved(model, old_model)

Im­ple­me­n­ta­ción de la conexión en Python con el bucle while y la es­tru­c­tu­ra try except

El es­ta­ble­ci­mie­n­to de la conexión puede fallar. Por lo tanto, es deseable intentar es­ta­ble­cer la conexión en varios intentos. Como no es posible saber de antemano cuántos intentos serán ne­ce­sa­rios, se recurre a un bucle while en Python. Además, se limita el número máximo de intentos. En el caso de que ninguno de los intentos tenga éxito, se aborta con un mensaje de error.

He aquí una solución es­que­má­ti­ca: se utiliza una sentencia try except para captar el error al es­ta­ble­cer la conexión. El uso de una sentencia break en el bloque try y una sentencia continue en el bloque except asegura que se itere de manera correcta. Si la conexión falla, se vuelve a intentar con continue. Si se establece la conexión, se termina el bucle mediante break:

max_tries = 10
attempt = 0
conn = None
# `continue` takes us here
while attempt < max_tries:
    attempt += 1
    print("Trying to get a connection")
    try:
        # might raise `ConnectionException`
        conn = get_connection()
        # got our connection
        break
    # `get_connection()` raised `ConnectionException`
    except ConnectionException:
        print("Something went wrong. Trying again")
        continue
# went through `max_tries` unsuccessful connection attempts
else:
    assert conn is None
    print("Unable to connect")
# `break` takes us here
assert conn is not None
print("Connection established")

Iterar sobre es­tru­c­tu­ras re­cu­r­si­vas con el bucle while de Python

El bucle while en Python es perfecto para resolver problemas re­cu­r­si­vos. A modo de ejemplo, el bucle es adecuado para iterar sobre:

  • listas anidadas
  • es­tru­c­tu­ras de árboles
  • gráficos

Descubre cómo funciona mediante el ejemplo de una matrioska. El conocido juguete consiste en muñecas dentro de muñecas. Desde el exterior no se puede ver cuántas muñecas hay, de modo que se procede de forma iterativa: se abre la muñeca exterior para ver lo que hay dentro. Si lo que se encuentra es otra matrioska, se repite el proceso. Un caso típico para emplear un bucle while.

Se diseña la matrioska como una lista anidada con un solo elemento en cada nivel. Se incluye otra lista o un objeto que no es una lista. Se itera sobre la matrioska siempre que se trate de una lista. Dentro del cuerpo del bucle se utiliza una asi­g­na­ción para pasar a un nivel inferior. En algún momento se encuentra un elemento que no sea una lista. Cuando llegue ese momento, se detiene la iteración:

def open_matroshka(matroshka):
    """
    * Matroshka dolls stacked five levels deep, with `None` inside:
    `matroshka = [[[[[None]]]]]`
    """
    while type(matroshka) is list:
        print("Opening the next matroshka")
        # go one level deeper
        matroshka = matroshka.pop()
    else:
        print(f"Reached the bottom and found {matroshka}")
        return matroshka
# test
matroshka = [[[[[None]]]]]
assert open_matroshka(matroshka) is None

Este sencillo enfoque funciona in­de­pe­n­die­n­te­me­n­te de la pro­fu­n­di­dad de los niveles anidados. El algoritmo desciende hasta el menor nivel y saca el elemento objetivo: la magia del bucle while de Python en acción.

Ejecutar un bucle while de Python en un tablero de juego

Un escenario de uso común de los bucles while en Python es el de mover una pieza por un tablero. Si quieres tener la garantía de que vas a recorrer todas las casillas, necesitas dos bucles anidados. Para ello, hay que tener en cuenta el tiempo de ejecución, ya que, si dispones de mucho tiempo, el programa puede apro­ve­char y funcionar durante todo este.

Se im­ple­me­n­ta un simple “Random Walk”, en el que una figura del tablero se mueve alea­to­ria­me­n­te hasta llegar a un destino. Este patrón de mo­vi­mie­n­to puede en­co­n­trar­se, por ejemplo, en el mo­vi­mie­n­to de una partícula en un líquido o en el mo­vi­mie­n­to de una mosca volando por el espacio. Como no se sabe de antemano cuántas ite­ra­cio­nes se van a necesitar, se recurre a un bucle while de Python.

Primero se define la función random_walk() que contiene el bucle while. Con el operador Python se comprueba si la posición actual es igual a la de destino. Si no, se sigue iterando:

def random_walk(board = (10, 10), goal = (4, 4), start = (0, 0)):
    # ensure arguments are valid
    if not (goal[0] in range(board[0]) and goal[1] in range(board[1]) and start[0] in range(board[0]) and start[1] in range(board[1])):
        print(f"Goal {goal} and / or start position {start} outside of board with dimensions {board}")
        return None, 0
    steps = 0
    pos = start
    # as long as we haven't reached the goal
    while not pos == goal:
        # move to neighboring position
        pos = get_neighbor(pos, board)
        steps += 1
        print(f"Moved to position {pos}")
    print(f"Reached goal at {pos} after {steps} steps")
    return pos, steps

Además, se define una función auxiliar get_neighbor() que retorna un posible campo alrededor de una de­te­r­mi­na­da posición:

def get_neighbor(pos, bounds):
     from random import choice
     """
          x = 0     . . .     m
                - - - - - - - -
      y = 0 |
              |
            . |              (x, y-1)
            . |  (x-1, y) (x, y)  (x+1, y)
            . |              (x, y+1)
              |
            n |
    
     """
     x, y = pos
     # computer neighbors
     neighbors = [ (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) ]
     # filter out neighbors that are outside of bounds
     neighbors = [ pos for pos in neighbors if 0 <= pos[0] < bounds[0] and 0 <= pos[1] < bounds[1] ]
     # select a random neighbor
     neighbor = choice(neighbors)
     return neighbor

A co­n­ti­nua­ción, se pone a prueba nuestra im­ple­me­n­ta­ción del random walk:

random_walk(board = (10, 10), goal = (4, 4), start = (5, 7))
Ir al menú principal