Guía para enumerar() en Python - Easy for Loops with Counting

En este tutorial, aprenda a usar la función enumerate() en Python. ¡Recorra listas, tuplas y cadenas con un índice para cada elemento automáticamente!

Introducción

Bucle con una variable de contador/índice: ¡un clásico en informática! Por lo general, definiría explícitamente una variable/índice de contador y la incrementaría manualmente en cada bucle, o usaría algún tipo de azúcar sintáctico para evitar este proceso a través de bucles for mejorados:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
some_list = ['Looping', 'with', 'counters', 'is', 'a', 'classic!']

# Manual counter incrementation
i = 0
for element in some_list:
    print(f'Element Index: {i}, Element: {element}')
    i += 1

# Automatic counter incrementation
for i in range(len(some_list)):
    print(f'Element Index: {i}, Element: {some_list[i]}')

Ambos fragmentos dan como resultado el mismo resultado:

1
2
3
4
5
6
Element Index: 0, Element: Looping
Element Index: 1, Element: with
Element Index: 2, Element: counters
Element Index: 3, Element: is
Element Index: 4, Element: a
Element Index: 5, Element: classic!

Debido a lo común que son los bucles como este en el trabajo diario, la función enumerate() se incorporó al espacio de nombres de Python. Puede, sin dependencias adicionales, recorrer un iterable en Python, con una variable/índice de contador automático con una sintaxis tan simple como:

1
2
for idx, element in enumerate(some_list):
     print(idx, element)

{.icon aria-hidden=“true”}

Nota: Es una convención común, pero no necesaria, nombrar el índice como idx si no se aplica ninguna otra etiqueta, ya que id es una palabra clave reservada. Por lo general, según el iterable con el que esté trabajando, se pueden atribuir nombres más significativos, como: batch_num, batch in enumerate(...).

Esta pieza de código da como resultado:

1
2
3
4
5
6
0 Looping
1 with
2 counters
3 is
4 a
5 classic!

¡Sumerjámonos en la función y exploremos cómo funciona! Es uno clásico y común, y al más puro estilo Python, simplifica una operación común y redundante y mejora la legibilidad de su código.

La función enumerate() en Python

La función enumerate() acepta una colección iterable (como una tupla, lista o cadena) y devuelve un objeto enumerate, que consta de un conjunto de claves y conjunto de valores, donde las claves corresponden a una variable de contador (que comienza en 0) y los valores corresponden a los elementos originales de la colección iterable:

1
2
3
obj = enumerate(some_list)
print(type(obj))
# <class 'enumerate'>

{.icon aria-hidden=“true”}

Nota: ¡El objeto enumerar es, en sí mismo, iterable! Puede usar la sintaxis estándar for, descomprimiendo las claves y valores del objeto enumerate.

Usando la sintaxis for estándar de Python, podemos desempaquetar las claves y los valores de este objeto e inspeccionar sus tipos:

1
2
3
4
5
6
7
8
9
for key, value in obj:
    print(type(key), type(value))
    
# <class 'int'> <class 'str'>
# <class 'int'> <class 'str'>
# <class 'int'> <class 'str'>
# <class 'int'> <class 'str'>
# <class 'int'> <class 'str'>
# <class 'int'> <class 'str'>

Los tipos de datos de los valores (elementos de la colección original) se conservan, por lo que incluso si pasa tipos de datos personalizados, siempre que sean una colección iterable válida, simplemente se anotarán con una variable de contador. Si tuviera que recopilar el objeto en sí mismo en una lista, su estructura sería muy clara:

1
2
print(list(obj))
# [(0, 'Looping'), (1, 'with'), (2, 'counters'), (3, 'is'), (4, 'a'), (5, 'classic!')]

Es solo un conjunto de tuplas con dos elementos cada una: una variable de contador, que comienza en 0, y cada elemento del iterable original asignado a los índices.

Puede establecer un argumento de inicio opcional, que denote no el índice de inicio en el iterable, sino el valor de inicio para el primer contador/índice que generará la función. Por ejemplo, digamos que nos gustaría comenzar en 1 en lugar de 0:

1
2
3
obj = enumerate(some_list, 1)
print(list(obj))
# [(1, 'Looping'), (2, 'with'), (3, 'counters'), (4, 'is'), (5, 'a'), (6, 'classic!')]

Bucle iterable con enumerate()

Habiendo dicho todo eso, recorrer un objeto enumerar tiene el mismo aspecto que recorrer otros iterables. El bucle for es útil aquí, ya que puede asignar variables de referencia a los valores de tupla devueltos. Además, no hay necesidad de hacer referencia explícita al objeto, ya que rara vez se usa fuera de un solo ciclo, por lo que el valor devuelto generalmente se usa directamente en el ciclo mismo:

1
2
3
# No need to assign the returned `enumerate` object to a distinct reference variable
for idx, element in enumerate(some_list):
     print(f'{idx}, {element}')

Esto resulta en:

1
2
3
4
5
6
0, Looping
1, with
2, counters
3, is
4, a
5, classic!

Si desea leer más sobre f-Strings y el formato de salida en Python, lea nuestra [Guía para el formato de cadenas con f-Strings de Python 3](/formateo-de-cadenas-con-python-3s -f-cuerdas/)!

¡Anotar cada elemento en un iterable, o más bien, incrementar un contador y devolverlo, mientras se accede a elementos de iterables es tan fácil como eso!

Vale la pena señalar que no sucede nada especial dentro de la función enumerate(). Realmente es, funcionalmente equivalente, al bucle inicial que escribimos, con una variable de contador explícita devuelta con un elemento. Si echas un vistazo a la nota en la documentación oficial, el resultado de la función es funcionalmente equivalente a:

1
2
3
4
5
def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

Puedes ver que el código es bastante similar a la primera implementación que hemos definido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Original implementation
i = 0
for element in some_list:
    print(f'Element Index: {i}, Element: {some_list[i]}')
    i += 1
    
# Or, rewritten as a method that accepts an iterable    
def our_enumerate(some_iterable, start=0):
    i = start
    for element in some_iterable:
        yield i, element
        i += 1

El punto clave aquí es: la palabra clave yield define un generador, que es iterable. Al devolver el índice y el elemento en sí, estamos creando un objeto generador iterable, que luego podemos recorrer y extraer elementos (y sus índices) a través del bucle for.

If you'd like to read more about the usage of the yield keyword here, read our Guía para entender la palabra clave "rendimiento" de Python!

Si tuviera que usar la función our_enumerate() en lugar de la función integrada, tendríamos los mismos resultados:

1
2
3
4
5
6
7
some_list = ['Looping', 'with', 'counters', 'is', 'a', 'classic!']

for idx, element in our_enumerate(some_list):
     print(f'{idx}, {element}')
        
obj = our_enumerate(some_list)
print(f'Object type: {obj}')

Esto resulta en:

1
2
3
4
5
6
7
0, Looping
1, with
2, counters
3, is
4, a
5, classic!
Object type: <generator object our_enumerate at 0x000002750B595F48>

La única diferencia es que solo tenemos un objeto generador genérico, en lugar de un nombre de clase más agradable.

Conclusión

En última instancia, la función enumerate() es simplemente azúcar sintáctica, que envuelve una implementación de bucle extremadamente común y directa.

En esta breve guía, hemos echado un vistazo a la función enumerate() en Python, el método de conveniencia incorporado para iterar sobre una colección y anotar los elementos con índices.

Licensed under CC BY-NC-SA 4.0