Enumerando colecciones con «enumerate»

Enumerando colecciones con «enumerate»



La función incorporada enumerate() permite, como lo indica su nombre, enumerar una colección (por ejemplo, una lista, una tupla, o cualquier otro objeto iterable). Opera recibiendo como argumento cualquier objeto iterable y retornando uno nuevo cuyos elementos son tuplas de dos elementos: el primero, un índice (empezando por el cero); el segundo, el elemento que se encuentra en esa posición en el objeto iterable original.

>>> lenguajes = ["Java", "C", "C++", "Rust", "Elixir"]
>>> list(enumerate(lenguajes))
[(0, 'Java'), (1, 'C'), (2, 'C++'), (3, 'Rust'), (4, 'Elixir')]

Para que la operación sea lo más eficiente posible, los elementos de enumerate() (es decir, las tuplas) se generan a medida que son utilizadas, de ahí que si queremos ver el resultado completo debamos convertirlo a una lista. La mayoría de las veces esto no será necesario puesto que estaremos usando esta función en un bucle. Por ejemplo, si queremos imprimir en pantalla cada uno de los lenguajes con su respectiva posición en la lista:

>>> for i, lenguaje in enumerate(lenguajes):
...     print(i, lenguaje)
...
0 Java
1 C
2 C++
3 Rust
4 Elixir

O si queremos imprimir cada uno de los lenguajes pero omitiendo el que está en la posición 2:

>>> for i, lenguaje in enumerate(lenguajes):
...     if i != 2:
...             print(lenguaje)
...
Java
C
Rust
Elixir

Podemos hacer que la función empiece a enumerar desde el uno o cualquier otro número pasándolo como segundo argumento:

>>> for i, lenguaje in enumerate(lenguajes, 1):
...     print(i, lenguaje)
...
1 Java
2 C
3 C++
4 Rust
5 Elixir

Decíamos que también puede aplicarse sobre otros objetos iterables. Por ejemplo, una cadena:

>>> letras = "abcdefghijklmnñopqrstuvwxyz"
>>> for i, letra in enumerate(letras, 1):
...     print(i, letra)
...
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
11 k
12 l
13 m
14 n
15 ñ
16 o
17 p
18 q
19 r
20 s
21 t
22 u
23 v
24 w
25 x
26 y
27 z

Ahora bien, si esta sintaxis de usar i, lenguaje o i, letra en un bucle «for» te resulta un tanto extraña, veamos en detalle cómo funciona. Primero recordemos la funcionalidad de unpacking o «desempaquetamiento». Consideremos la siguiente tupla:

>>> t = (1, 2, 3)

Si queremos tener los valores 1, 2 y 3 en tres variables distintas, deberíamos hacer:

>>> a = t[0]
>>> b = t[1]
>>> c = t[2]

No obstante, usando la funcionalidad de desempaquetamiento, tenemos un método más compacto:

>>> (a, b, c) = t
>>> a
1
>>> b
2
>>> c
3

Los paréntesis pueden ser omitidos, de modo que lo siguiente también hace lo mismo:

>>> a, b, c = t

Pues bien, volvamos al ejemplo que queríamos explicar que era este:

for i, lenguaje in enumerate(lenguajes):
    print(i, lenguaje)

Vayamos paso por paso. Sabemos que cada uno de los elementos generados por enumerate() es una tupla; entonces, el siguiente código imprime cada una de esas tuplas.

>>> for t in enumerate(lenguajes):
...     print(t)
...
(0, 'Java')
(1, 'C')
(2, 'C++')
(3, 'Rust')
(4, 'Elixir')

El primer elemento de cada una de esas tuplas era un índice o posición, mientras que el segundo era un elemento de lenguajes. Podemos asignarlos en dos variables diferentes:

>>> for t in enumerate(lenguajes):
...     indice = t[0]
...     lenguaje = t[1]
...     print(indice, lenguaje)
...
0 Java
1 C
2 C++
3 Rust
4 Elixir

Pero usando la propiedad de unpacking que acabamos de explicar, puede compactarse así:

>>> for t in enumerate(lenguajes):
...     indice, lenguaje = t
...     print(indice, lenguaje)
...

Aún más: dado que t solamente se usa para asignar los valores a indice y lenguaje, Python nos permite ubicar ese patrón (es decir, el fragmento indice, lenguaje) directamente una línea más arriba, sin necesidad de crear una variable intermedia (t).

>>> for indice, lenguaje in enumerate(lenguajes):
...     print(indice, lenguaje)
...

Y abreviando indice como i obtenemos el código que tratabamos de explicar:

>>> for i, lenguaje in enumerate(lenguajes):
...     print(i, lenguaje)
...
0 Java
1 C
2 C++
3 Rust
4 Elixir

Algunos programadores, acostumbrados a otros lenguajes, suelen hacer esto en lugar de usar enumerate():

>>> for i in range(len(lenguajes)):
...     print(i, lenguajes[i])
...
0 Java
1 C
2 C++
3 Rust
4 Elixir

Este método es menos eficiente (implica llamar una función extra) y menos legible.



Deja un comentario