Curso

En este tutorial veremos qué es la función enumerate() de Python, cuándo se utiliza, qué tipo de objetos crea, su sintaxis y cómo funciona. Después, revisaremos ejemplos enumerando distintos tipos de objetos en Python, las alternativas a enumerate() y por qué suelen ser menos adecuadas para la misma tarea.
¿Qué es enumerate en Python y cuándo se usa?
enumerate() es una función incorporada de Python que recibe un iterable (una colección de elementos o cualquier objeto de Python que soporte iteración, como tuplas, listas, conjuntos o cadenas), recorre sus elementos internamente y devuelve un objeto de tipo enumerate. En otras palabras, asigna un contador que aumenta de 1 en 1 a cada elemento del iterable y nos ayuda a llevar la cuenta de las iteraciones al recorrer ese objeto. Tras crear un objeto enumerate, podemos convertirlo en una lista, una tupla o un diccionario (usando list(), tuple() o dict(), respectivamente), o bien iterar directamente sobre él para acceder tanto a los elementos como a sus índices correspondientes.
Usar enumerate() resulta útil por su eficiencia en memoria y computación frente a bucles for convencionales, ya que devuelve el índice y el elemento correspondiente de una sola vez.
Sintaxis
La sintaxis de enumerate() es muy sencilla:
enumerate(iterable, start=0)
Aquí, iterable es el objeto que queremos enumerar, y start es un parámetro opcional que define el índice inicial del contador (por defecto, será el índice típico basado en 0 de Python).
Ten en cuenta que aplicar enumerate() a un iterable no modifica el objeto original. En su lugar, el objeto enumerate resultante debe asignarse a una variable si queremos seguir usándolo.
Ahora, veamos cómo funciona esta función con distintos tipos de iterables de Python.
Enumerar una lista
Primero, aplicamos enumerate() a una lista de Python:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
print(type(enumerated_drinks))
print(enumerated_drinks)
Salida:
<class 'enumerate'>
<enumerate object at 0x000002DC3A1145C0>
El tipo del objeto resultante es enumerate y, al imprimirlo, solo confirmamos de nuevo el tipo de objeto y dónde se almacena en memoria. Probemos a acceder al primer elemento de este objeto igual que lo haríamos con una lista o tupla de Python:
print(enumerated_drinks[0])
Salida:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5948/1233607596.py in <module>
----> 1 print(enumerated_drinks[0])
TypeError: 'enumerate' object is not subscriptable
Obtenemos un error que indica que no podemos acceder a los componentes de un objeto enumerate de ese modo. Para ello, debemos convertirlo en lista, tupla o diccionario, o iterar directamente sobre el objeto enumerate. Convirtámoslo en una lista:
list_enumerated_drinks = list(enumerated_drinks)
print(list_enumerated_drinks)
Salida:
[(0, 'tea'), (1, 'coffee'), (2, 'cappuccino'), (3, 'lemonade')]
Ahora vemos claramente la estructura interna de la lista resultante: cada elemento es una tupla de 2 componentes donde el primero es el índice del elemento correspondiente en la lista original y el segundo es el propio elemento. Podemos acceder a sus elementos con indexación normal:
print(list_enumerated_drinks[0])
print(list_enumerated_drinks[0][1])
Salida:
(0, 'tea')
tea
Ahora convirtamos un objeto enumerate creado a partir de la lista original en una tupla. Además, esta vez fijaremos el parámetro opcional start a 1:
tuple_enumerated_drinks = tuple(enumerate(drinks, start=1))
print(tuple_enumerated_drinks)
Salida:
((1, 'tea'), (2, 'coffee'), (3, 'cappuccino'), (4, 'lemonade'))
La estructura interna de la tupla anterior es idéntica a la de la lista creada antes. Sin embargo, esta vez hemos cambiado el contador para que empiece en 1, más "humano" que el índice basado en 0 de Python.
Hay un método interesante que conviene mencionar: next(). Volvamos a crear enumerated_drinks y probemos este método en acción:
enumerated_drinks = enumerate(drinks)
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
Salida:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5948/330134057.py in <module>
4 print(next(enumerated_drinks))
5 print(next(enumerated_drinks))
----> 6 print(next(enumerated_drinks))
StopIteration:
Como ves, cada vez que llamamos a este método sobre un objeto enumerate, devuelve el siguiente elemento (y comprobamos de nuevo que cada elemento es una tupla de 2 componentes con el par contador-elemento). Cuando se agotan todos los elementos, el objeto enumerate queda exhausto y volver a llamar a next() lanza un error StopIteration. De hecho, si ahora intentamos convertir ese objeto en una lista, veremos que está vacía:
print(list(enumerated_drinks))
Salida:
[]
Esto significa que en cada llamada a next() se «consume» un elemento del objeto enumerate; cada vez se hace más pequeño hasta agotarse por completo. Veremos esta propiedad también más adelante al usar bucles for sobre ellos.
Si quieres aprender más sobre listas de Python y otros conceptos básicos, puedes hacer el curso Introduction to Python de DataCamp.
Enumerar una tupla
Aplicar enumerate() a una tupla es idéntico a hacerlo con una lista:
drinks = ('tea', 'coffee', 'cappuccino', 'lemonade')
enumerated_drinks = enumerate(drinks)
print(enumerated_drinks)
Salida:
<enumerate object at 0x000002DC3A098D40>
De nuevo, obtenemos un objeto enumerate cuyos elementos no son accesibles con la indexación habitual. Transformémoslo esta vez en un diccionario:
dict_enumerated_drinks = dict(enumerated_drinks)
print(dict_enumerated_drinks)
Salida:
{0: 'tea', 1: 'coffee', 2: 'cappuccino', 3: 'lemonade'}
Cada par clave:valor del diccionario resultante representa el par contador-elemento correspondiente. Podemos acceder a los valores del diccionario de la forma habitual en Python:
print(dict_enumerated_drinks[0])
print(dict_enumerated_drinks[3])
Salida:
tea
lemonade
Si quieres profundizar en diccionarios de Python y pandas, echa un vistazo al curso Intermediate Python.
Enumerar una cadena
Como las cadenas de Python son secuencias de caracteres y por tanto objetos iterables, también podemos crear un enumerate de una cadena:
enumerated_hello_world = enumerate('Hello, World!', start=10)
print(enumerated_hello_world)
Salida:
<enumerate object at 0x000002DC3A145C40>
Como antes, convirtamos este objeto en una lista:
list_enumerated_hello_world = list(enumerated_hello_world)
print(list_enumerated_hello_world)
Salida:
[(10, 'H'), (11, 'e'), (12, 'l'), (13, 'l'), (14, 'o'), (15, ','), (16, ' '), (17, 'W'), (18, 'o'), (19, 'r'), (20, 'l'), (21, 'd'), (22, '!')]
Vemos las ya familiares tuplas de 2 elementos, donde el primero es el contador del carácter correspondiente (en este caso, empezando en 10) y el segundo es el propio carácter. El espacio en blanco y los signos de puntuación también cuentan.
Volvamos a crear enumerated_hello_world, esta vez sin start=10, y apliquemos next sobre él:
enumerated_hello_world = enumerate('Hello, World!')
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
print(next(enumerated_hello_world))
# Crear una lista con lo que queda del objeto enumerate
print(list(enumerated_hello_world))
Salida:
(0, 'H')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')
(5, ',')
[(6, ' '), (7, 'W'), (8, 'o'), (9, 'r'), (10, 'l'), (11, 'd'), (12, '!')]
En la última línea dejamos de llamar a next() y convertimos enumerated_hello_world en una lista. La lista resultante contiene los caracteres que quedaban en el objeto enumerate tras varias llamadas a next().
Enumerar con un bucle for
Por último, veamos cómo funciona enumerate() junto con un bucle for:
for item in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(item)
Salida:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
En cada iteración se devuelve la tupla contador-elemento, igual que ocurre al usar next(). Aunque no se aprecie en el fragmento anterior, el objeto enumerate también va perdiendo elementos (las tuplas de 2 componentes) una a una en cada iteración. Para demostrarlo, reescribamos el ejemplo y comprobemos la longitud de la lista resultante tras terminar todas las iteraciones:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
for item in enumerated_drinks:
print(item)
print(list(enumerated_drinks))
Salida:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
[]
Igual que al usar next(), iterar sobre un objeto enumerate lo va agotando poco a poco.
Volvamos al ejemplo anterior. ¿Cómo accedemos a cada elemento por separado dentro de cada tupla? Para ello, añadimos una variable de iteración más y desempaquetamos las tuplas:
for count, drink in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(count, drink)
Salida:
0 tea
1 coffee
2 cappuccino
3 lemonade
Arriba, devolvemos dos variables por cada tupla: una para el contador y otra para el elemento.
Ten en cuenta que también podemos implementar lo mismo sin usar enumerate(). Por ejemplo, podemos introducir una variable contador fuera del bucle e incrementarla en 1 en cada iteración:
count = 0
for drink in ['tea', 'coffee', 'cappuccino', 'lemonade']:
print(count, drink)
count += 1
Salida:
0 tea
1 coffee
2 cappuccino
3 lemonade
Como alternativa, podemos usar la combinación de range() y len() sobre una lista (o tupla, cadena, etc.) predefinida:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
for count in range(len(drinks)):
print(count, drinks[count])
Salida:
0 tea
1 coffee
2 cappuccino
3 lemonade
Además, en ambas alternativas a enumerate() también podemos ajustar el contador para que no empiece en cero:
count = 1
for drink in ['tea', 'coffee', 'cappuccino', 'lemonade']:
print(count, drink)
count += 1
print('\n')
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
for count in range(len(drinks)):
print(count + 1, drinks[count])
Salida:
1 tea
2 coffee
3 cappuccino
4 lemonade
1 tea
2 coffee
3 cappuccino
4 lemonade
Sin embargo, comparadas con enumerate(), ambas aproximaciones son más aparatosas, propensas a errores (por ejemplo, podemos olvidarnos de actualizar el contador manualmente) y requieren más código. Por eso, usar enumerate() es la forma más pythónica siempre que necesites acceder al contador y al elemento de un iterable en cada iteración.
Conclusión
En este tutorial hemos aprendido muchas cosas sobre enumerate() en Python: para qué sirve, qué alternativas tiene y por qué suele ser más eficaz que ellas. enumerate() quizá no sea familiar para quienes empiezan con Python, e incluso programadores avanzados pueden no tener presente esta función tan útil.
Si te gustaría profundizar en enumerate(), prueba el curso Python Data Science Toolbox (Part 2), que explora iteradores e iterables en detalle y ofrece ejercicios interactivos para poner en práctica nuevas habilidades. Además, si quieres dominar otros conceptos de Python, quizá te interese Learn Python with DataCamp, que incluye varios itinerarios de aprendizaje y de carrera, chuletas, tutoriales, certificaciones en Python y pruebas de evaluación.