Bucles¶
(1)
Cuando queremos hacer algo más de una vez, necesitamos recurrir a un bucle. En esta sección veremos las distintas sentencias en Python que nos permiten repetir un bloque de código.
La sentencia while
¶
El primer mecanismo que existe en Python para repetir instrucciones es usar la sentencia while
. La semántica tras esta sentencia es: «Mientras se cumpla una condición1 haz algo».
Veamos un primer ejemplo con un sencillo bucle que repite un saludo mientras así se desee:
>>> want_greet = 'S'#(1)!
>>> while want_greet == 'S':#(2)!
... print('Hola qué tal!')
... want_greet = input('¿Quiere otro saludo? [S/N] ')
... print('Que tenga un buen día')
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] N
Que tenga un buen día
- Importante dar un valor inicial.
-
- La condición del bucle se comprueba en cada nueva iteración (repetición).
- En este caso chequeamos que la variable
want_greet
sea igual a'S'
.
Iteración
En este contexto, llamamos iteración a cada «repetición» del bucle. Iterar significa «repetir» una determinada acción.
Romper un bucle while¶
Python ofrece la posibilidad de romper o finalizar un bucle antes de que se cumpla la condición de parada.
Supongamos que en el ejemplo anterior establecemos un máximo de 4 saludos:
>>> MAX_GREETS = 4
>>> num_greets = 0
>>> want_greet = 'S'
>>> while want_greet == 'S':
... print('Hola qué tal!')
... num_greets += 1
... if num_greets == MAX_GREETS:
... print('Número máximo de saludos alcanzado')
... ┌────── break
... ▼ want_greet = input('¿Quiere otro saludo? [S/N] ')
... print('Que tenga un buen día')
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
Número máximo de saludos alcanzado
Que tenga un buen día
Como hemos visto en este ejemplo, break
nos permite finalizar el bucle una vez que hemos llegado al máximo número de saludos. Pero si no hubiéramos llegado a dicho límite, el bucle habría seguido hasta que el usuario indicara que no quiere más saludos.
Solución alternativa
Otra forma de resolver este ejercicio sería incorporar la (segunda) condición al bucle:
Comprobar la rotura¶
Python nos ofrece la posibilidad de detectar si el bucle ha acabado de forma ordinaria, esto es, ha finalizado por no cumplirse la condición establecida.
Para ello podemos hacer uso de la sentencia else
como parte del propio bucle. Si el bucle while
finaliza normalmente (sin llamada a break) el flujo de control pasa a la sentencia opcional else
.
Veamos su comportamiento siguiendo con el ejemplo que venimos trabajando:
>>> MAX_GREETS = 4
>>> num_greets = 0
>>> want_greet = 'S'
>>> while want_greet == 'S':
... print('Hola qué tal!')
... num_greets += 1
... if num_greets == MAX_GREETS:
... print('Máximo número de saludos alcanzado')
... break
... want_greet = input('¿Quiere otro saludo? [S/N] ')
... else:#(1)!
... print('Usted no quiere más saludos')
... print('Que tenga un buen día')
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] N
Usted no quiere más saludos
Que tenga un buen día
- El flujo de control entrará por aquí cuando
want_greet
sea distinto de'S'
y por tanto no se cumpla la condición del bucle.
Contexto
La sentencia else
sólo tiene sentido en aquellos bucles que contienen un break
.
Continuar un bucle while¶
Hay situaciones en las que, en vez de romper un bucle, nos interesa saltar adelante hacia la siguiente iteración.
Para ello Python nos ofrece la sentencia continue
que hace precisamente eso, descartar el resto del código del bucle y saltar a la siguiente iteración.
Continuamos con el ejemplo anterior pero ahora vamos a contar el número de respuestas válidas:
>>> want_greet = 'S'
>>> valid_options = 0
>>> while want_greet == 'S':
... ▲ print('Hola qué tal!')
... │ want_greet = input('¿Quiere otro saludo? [S/N] ')
... │ if want_greet not in 'SN':#(1)!
... │ print('No le he entendido pero le saludo')
... │ want_greet = 'S'#(2)!
... └───── continue#(3)!
... valid_options += 1
... print(f'{valid_options} respuestas válidas')
... print('Que tenga un buen día')
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] A
No le he entendido pero le saludo
Hola qué tal!
¿Quiere otro saludo? [S/N] B
No le he entendido pero le saludo
Hola qué tal!
¿Quiere otro saludo? [S/N] N
2 respuestas válidas
Que tenga un buen día
- Comprobamos si la entrada es un «sí» o un «no».
- Asignamos «sí» a la opción para que el bucle pueda seguir funcionando.
- Saltamos de nuevo al comienzo del bucle.
Bucle infinito¶
Si no establecemos correctamente la condición de parada o bien el valor de alguna variable está fuera de control, es posible que lleguemos a una situación de bucle infinito, del que nunca podamos salir.
Veamos un ejemplo de esto:
>>> num = 1
>>> while num != 10:
... num += 2
...
^CTraceback (most recent call last):
Cell In[4], line 1
while num != 10:
KeyboardInterrupt
El problema que surje es que la variable num
toma los valores 1, 3, 5, 7, 9, 11, ... por lo que nunca se cumple la condición de parada del bucle. Esto hace que repitamos «eternamente» la instrucción de incremento.
Parar el bucle
Para abortar una situación de bucle infinito podemos pulsar en el teclado la combinación Ctrl+C. Se puede ver reflejado en el intérprete de Python por KeyboardInterrupt
.
Una posible solución a este problema sería reescribir la condición de parada en el bucle:
Bucles infinitos como recurso¶
Hay ocaciones en las que un supuesto bucle «infinito» puede ayudarnos a resolver un problema.
Si retomamos el ejemplo de los saludos, podríamos reescribirlo utilizando un «bucle infinito» de la siguiente manera:
>>> while True:#(1)!
... print('Hola qué tal!')
... if (want_greet := input('¿Quiere otro saludo? [S/N] ')) != 'S':#(2)!
... break#(3)!
... print('Que tenga un buen día')
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] S
Hola qué tal!
¿Quiere otro saludo? [S/N] N
Que tenga un buen día
- Este bucle por sí solo implicaría un bucle infinito, pero veremos que no es así.
- Usando el operador morsa pedimos la entrada y comprobamos su valor.
- La sentencia
break
nos «salva» de este bucle infinito cuando no se quieren más saludos.
En comparación con el enfoque «clásico» del bucle while
:
- Como ventaja podemos observar que no es necesario asignar un valor inicial a
want_greet
antes de entrar al bucle. - Como desventaja el código resulta menos «idiomático» ya que la condición del bucle no nos da ninguna pista de lo que está ocurriendo.
Ejercicio
pypas m5-limited
La sentencia for
¶
Python permite recorrer aquellos tipos de datos que sean iterables, es decir, que admitan iterar2 sobre ellos. Algunos ejemplos de tipos y estructuras de datos iterables (que permiten ser iteradas/recorridas) son: cadenas de texto, listas, tuplas, diccionarios, ficheros, etc.
La sentencia for
nos permite realizar esta acción.
A continuación se plantea un ejemplo en el que recorremos (iteramos sobre) una cadena de texto:
-
- La variable
letter
va tomando cada uno de los elementos deword
. - Dado que
word
es una cadena de texto, cada elemento es un caracter.
- La variable
Variable de asignación
La variable de asignación que utilizamos en el bucle for
para ir tomando los valores puede tener cualquier nombre. Al fin y al cabo es una variable que definimos según nuestras necesidades.
Suele ser de buen estilo que sea un nombre en singular relacionado con la estructura que recorre:
for item in items:
for num in numbers:
for product in products:
for line in lines:
Romper un bucle for¶
Una sentencia break
dentro de un for
rompe el bucle, igual que veíamos para los bucles while
.
Veamos un ejemplo recorriendo una cadena de texto y parando el bucle cuando encontremos una letra t minúscula:
>>> word = 'Python'
>>> for letter in word:
... if letter == 't':
... break
... print(letter)
...
P
y
Comprobación de rotura y continuación
Tanto la comprobación de rotura de un bucle como la continuación a la siguiente iteración se llevan a cabo del mismo modo que hemos visto con los bucles de tipo while
.
Ejercicio
pypas count-vowels
Secuencias de números¶
Es muy habitual hacer uso de secuencias de números en bucles. Python no tiene una instrucción específica para ello. Lo que sí aporta es una función range()
que devuelve un flujo de números en el rango especificado3.
La técnica para la generación de secuencias de números es muy similar a la utilizada en los «slices» de cadenas de texto. En este caso disponemos de la función range(start, stop, step)
:
Parámetro | Carácter | Por defecto |
---|---|---|
start |
Opcional | 0 |
stop |
Obligatorio | - |
step |
Opcional | 1 |
Veamos distintos casos de uso:
start=0
,stop=3
,step=1
- También se podría haber escrito
range(0, 3)
pero es innecesario. - Recordar que al ser índice 0, el rango llega hasta 1 menos que el límite superior.
- También se podría haber escrito
i, j, k
Se suelen utilizar nombres de variables i
, j
, k
para lo que se denominan contadores4. Este tipo de variables toman valores numéricos enteros como en los ejemplos anteriores.
No conviene generalizar el uso de estas variables a situaciones en las que, claramente, tenemos la posibilidad de asignar un nombre semánticamente más significativo.
Ejercicio
pypas prime
Usando el guión bajo¶
Hay situaciones en las que no necesitamos usar la variable que toma valores en el rango, sino que únicamente queremos repetir una acción un número determinado de veces.
Para estos casos se suele recomendar usar el guión bajo _
como nombre de variable, que da a entender que no estamos usando esta variable de forma explícita:
>>> for _ in range(10):
... print('Repeat me 10 times!')
...
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Ejercicio
pypas pow
Bucles anidados¶
Como ya vimos en las sentencias condicionales, el anidamiento es una técnica por la que incluimos distintos niveles de encapsulamiento de sentencias, unas dentro de otras, con mayor nivel de profundidad.
En el caso de los bucles también es posible hacer anidamiento, tanto para bucles while como para bucles for.
(1)
-
- Muñecas rusas «Matrioskas» para ejemplificar el anidamiento.
- Marina Yufereva Escáner Cultural
Veamos un ejemplo de 2 bucles anidados en el que generamos todas las tablas de multiplicar:
>>> for num_table in range(1, 10):#(1)!
... for mul_factor in range(1, 10):#(2)!
... result = num_table * mul_factor
... print(f'{num_table} * {mul_factor} = {result}')
... print('----------')
...
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
1 * 4 = 4
1 * 5 = 5
1 * 6 = 6
1 * 7 = 7
1 * 8 = 8
1 * 9 = 9
----------
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
2 * 8 = 16
2 * 9 = 18
----------
3 * 1 = 3
3 * 2 = 6
3 * 3 = 9
3 * 4 = 12
3 * 5 = 15
3 * 6 = 18
3 * 7 = 21
3 * 8 = 24
3 * 9 = 27
----------
4 * 1 = 4
4 * 2 = 8
4 * 3 = 12
4 * 4 = 16
4 * 5 = 20
4 * 6 = 24
4 * 7 = 28
4 * 8 = 32
4 * 9 = 36
----------
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
5 * 8 = 40
5 * 9 = 45
----------
6 * 1 = 6
6 * 2 = 12
6 * 3 = 18
6 * 4 = 24
6 * 5 = 30
6 * 6 = 36
6 * 7 = 42
6 * 8 = 48
6 * 9 = 54
----------
7 * 1 = 7
7 * 2 = 14
7 * 3 = 21
7 * 4 = 28
7 * 5 = 35
7 * 6 = 42
7 * 7 = 49
7 * 8 = 56
7 * 9 = 63
----------
8 * 1 = 8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
----------
9 * 1 = 9
9 * 2 = 18
9 * 3 = 27
9 * 4 = 36
9 * 5 = 45
9 * 6 = 54
9 * 7 = 63
9 * 8 = 72
9 * 9 = 81
----------
- Para cada valor que toma la variable
num_table
la otra variablemul_factor
toma todos sus valores. - Como resultado tenemos una combinación completa de los valores en el rango especificado.
Complejidad ciclomática
Podemos añadir todos los niveles de anidamiento que queramos. Eso sí, hay que tener en cuenta que cada nuevo nivel de anidamiento supone un importante aumento de la complejidad ciclomática de nuestro código, lo que se traduce en mayores tiempos de ejecución.
Ejercicio
pypas mosaic
Ejercicios¶
- pypas
letdig
- pypas
m3-sum-limited
- pypas
repeat-please
- pypas
one-tree
- pypas
chess-horse
- pypas
domino
- pypas
fmin
- pypas
ascii-table
- pypas
guess-number
- pypas
gcd
- pypas
hamming
- pypas
cartesian
- pypas
cumprod-sq
- pypas
isalpha
- pypas
kpower
- pypas
fibonacci
-
Esta condición del bucle se conoce como condición de parada. ↩
-
Realizar cierta acción varias veces. En este caso la acción es tomar cada elemento. ↩
-
Una de las grandes ventajas es que la «lista» generada no se construye explícitamente, sino que cada valor se genera bajo demanda. Esta técnica mejora el consumo de recursos, especialmente en términos de memoria. ↩
-
Esto viene de tiempos antiguos en FORTRAN donde
i
era la primera letra que tenía valor entero por defecto. ↩