Módulo 1 · Lección 18 de 59
La función range
Generar secuencias de números para recorrerlas con un for, con inicio, fin y paso, incluido el paso negativo.
El for recorre secuencias, y eso está muy bien cuando tienes una cadena o una lista delante. Pero la mitad de las veces lo que quieres es repetir algo diez veces, o recorrer los números del 1 al 100. Ahí no hay ninguna secuencia que recorrer: hay que fabricarla.
range es la función que la fabrica, y está hecha exactamente para combinarse con for.
range con un solo argumento#
for numero in range(5):
print(numero)0
1
2
3
4Cinco vueltas, pero los números van del 0 al 4. Esto es lo primero que hay que interiorizar: range(5) produce cinco números empezando en cero, y el 5 no sale.
range con inicio y fin#
Si el cero no te sirve, di desde dónde empezar:
for numero in range(1, 6):
print(numero)1
2
3
4
5El primer argumento es el inicio, incluido; el segundo es el fin, excluido. De ahí el 6 para llegar hasta el 5.
Esta es la forma que usarás para cualquier cosa que se cuente como cuentan las personas, empezando por el uno:
for multiplicador in range(1, 11):
print(f"7 x {multiplicador:>2} = {7 * multiplicador:>3}")7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70range con paso#
El tercer argumento es cuánto avanzar en cada salto:
for numero in range(0, 21, 5):
print(numero)0
5
10
15
20El paso por defecto es 1, que es lo que se aplicaba en los ejemplos anteriores. Con paso, el final sigue estando excluido: el rango va dando saltos y se detiene en el último número que no alcance el fin. Por eso range(0, 21, 5) llega hasta el 20, mientras que range(0, 20, 5) se queda en el 15.
Paso negativo#
Un paso negativo cuenta hacia atrás. Entonces el inicio tiene que ser mayor que el fin:
for numero in range(10, 0, -1):
print(numero)10
9
8
7
6
5
4
3
2
1Ahí está otra vez el fin excluido, y es la trampa habitual: para llegar hasta el 1 hay que poner un 0 como fin. Si de verdad quieres que salga el cero, range(10, -1, -1).
MINUTOS = 3
for restantes in range(MINUTOS * 60, -1, -30):
minutos = restantes // 60
segundos = restantes % 60
print(f"Quedan {minutos}:{segundos:02d}")
print("Tiempo agotado.")Quedan 3:00
Quedan 2:30
Quedan 2:00
Quedan 1:30
Quedan 1:00
Quedan 0:30
Quedan 0:00
Tiempo agotado.range no es una lista#
Imprime un range a secas y verás algo inesperado:
print(range(5))
print(type(range(5)))range(0, 5)
<class 'range'>No sale [0, 1, 2, 3, 4] porque range no contiene los números: contiene la receta para generarlos. Guarda solo tres datos, el inicio, el fin y el paso, y va calculando cada número en el momento en que el for se lo pide.
Eso es una decisión de diseño deliberada y muy útil: range(1000000) ocupa lo mismo que range(5), unas pocas decenas de bytes, mientras que una lista con un millón de números ocuparía megabytes. Puedes recorrer rangos enormes sin gastar memoria.
Cuando quieras ver el contenido, pide que se construya la lista con list():
print(list(range(5)))
print(list(range(1, 6)))
print(list(range(0, 21, 5)))
print(list(range(10, 0, -1)))
print(list(range(5, 1)))[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[0, 5, 10, 15, 20]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[]len() también funciona sobre un range y te dice cuántas vueltas dará el bucle sin necesidad de generarlo:
print(len(range(5)))
print(len(range(0, 21, 5)))5
5Tabla de referencia#
| Expresión | Números que produce | Cuántos |
|---|---|---|
range(5) | 0, 1, 2, 3, 4 | 5 |
range(1, 5) | 1, 2, 3, 4 | 4 |
range(1, 6) | 1, 2, 3, 4, 5 | 5 |
range(0, 10, 2) | 0, 2, 4, 6, 8 | 5 |
range(0, 10, 3) | 0, 3, 6, 9 | 4 |
range(10, 0, -1) | 10, 9, 8, ... 2, 1 | 10 |
range(10, -1, -2) | 10, 8, 6, 4, 2, 0 | 6 |
range(5, 1) | ninguno | 0 |
range(0, 10, -1) | ninguno | 0 |
Recorrer un texto por posiciones#
Con range(len(texto)) puedes recorrer las posiciones en lugar de los caracteres, que es lo que necesitas cuando además del carácter te interesa dónde está:
palabra = "Python"
for posicion in range(len(palabra)):
print(f"{posicion}: {palabra[posicion]}")0: P
1: y
2: t
3: h
4: o
5: nlen(palabra) es 6, así que range(6) da del 0 al 5, que son exactamente las posiciones válidas. El fin excluido encaja aquí de forma perfecta, y no es casualidad: es el motivo por el que se diseñó así.
Los errores de uno de más y uno de menos#
Son tan frecuentes que tienen nombre propio en inglés, off-by-one, y casi siempre salen de olvidar que el final está excluido.
| Lo que quieres | Mal | Bien |
|---|---|---|
| Del 1 al 10 | range(1, 10) se queda en 9 | range(1, 11) |
| Diez vueltas | range(1, 10) da nueve | range(10) o range(1, 11) |
| Las posiciones de un texto | range(len(texto) + 1) se sale | range(len(texto)) |
| Del 10 al 1 hacia atrás | range(10, 1, -1) se queda en 2 | range(10, 0, -1) |