Curso

Neste tutorial, vamos ver o que é a função enumerate() em Python, quando ela é usada, que tipo de objeto ela cria, qual é a sua sintaxe e como funciona. Em seguida, veremos alguns exemplos enumerando diferentes tipos de objetos em Python, as alternativas à função enumerate() e por que elas são menos adequadas para a mesma tarefa.
O que é enumerate em Python e quando é usada?
A função enumerate() é uma função nativa do Python que recebe um iterável (uma coleção de itens ou qualquer outro objeto Python que suporte iteração, como tuplas, listas, conjuntos ou strings), percorre seus itens nos bastidores e retorna um objeto do tipo enumerate. Em outras palavras, essa função atribui uma contagem que incrementa de 1 em 1 para cada item de um iterável e ajuda a acompanhar as iterações enquanto fazemos loop sobre esse objeto. Depois de criar um objeto enumerate, podemos convertê-lo em uma lista, uma tupla ou um dicionário (usando as funções list(), tuple() ou dict(), respectivamente), ou percorrer esse objeto diretamente para acessar tanto os itens quanto seus respectivos índices.
Usar a função enumerate() é útil do ponto de vista de eficiência de memória e computação em comparação com for-loops, já que ela retorna o índice e o item correspondente de uma vez só.
Sintaxe
A sintaxe da função enumerate() é bem simples:
enumerate(iterable, start=0)
Aqui, iterable é o objeto iterável que queremos enumerar, e start é um parâmetro opcional que define o índice inicial do contador (por padrão, ele será o índice 0 típico do Python).
Repare que aplicar a função enumerate() a um iterável não altera o objeto original. Em vez disso, o objeto enumerate resultante deve ser atribuído a uma variável se quisermos usá-lo depois.
Agora, vamos ver como essa função funciona com diferentes tipos de iteráveis do Python.
Enumerando uma lista
Primeiro, vamos aplicar a função enumerate() a uma lista em Python:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
print(type(enumerated_drinks))
print(enumerated_drinks)
Saída:
<class 'enumerate'>
<enumerate object at 0x000002DC3A1145C0>
O tipo do objeto resultante é enumerate e, quando tentamos imprimi-lo, apenas confirmamos novamente o tipo do objeto e onde ele está armazenado na memória do computador. Vamos tentar acessar o primeiro elemento desse objeto da mesma forma que faríamos com uma lista ou tupla em Python:
print(enumerated_drinks[0])
Saída:
---------------------------------------------------------------------------
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
Recebemos um erro indicando que os componentes de um objeto enumerate não são acessíveis dessa maneira. Em vez disso, para esse fim, precisamos convertê-lo em uma lista, tupla ou dicionário, ou percorrer diretamente o objeto enumerate. Vamos converter nosso objeto enumerate em uma lista:
list_enumerated_drinks = list(enumerated_drinks)
print(list_enumerated_drinks)
Saída:
[(0, 'tea'), (1, 'coffee'), (2, 'cappuccino'), (3, 'lemonade')]
Agora vemos claramente a estrutura interna da lista resultante: cada item é uma tupla de 2 elementos em que o primeiro é o índice do item correspondente na lista original e o segundo é o próprio item. Podemos usar a indexação comum para acessar os elementos dessa lista:
print(list_enumerated_drinks[0])
print(list_enumerated_drinks[0][1])
Saída:
(0, 'tea')
tea
Agora, vamos converter um objeto enumerate criado a partir da lista original em uma tupla. Além disso, desta vez vamos definir o parâmetro opcional start como 1:
tuple_enumerated_drinks = tuple(enumerate(drinks, start=1))
print(tuple_enumerated_drinks)
Saída:
((1, 'tea'), (2, 'coffee'), (3, 'cappuccino'), (4, 'lemonade'))
A estrutura interna da tupla acima é idêntica à da lista criada anteriormente. Porém, desta vez, também alteramos a contagem, deixando-a mais “estilo humano” em vez da indexação baseada em zero do Python.
Há um método interessante de um objeto enumerate que vale mencionar aqui: o método next(). Vamos recriar o objeto enumerated_drinks e testar esse método na prática:
enumerated_drinks = enumerate(drinks)
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
print(next(enumerated_drinks))
Saída:
(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 vemos, toda vez que chamamos esse método em um objeto enumerate, ele retorna o próximo item (e podemos confirmar novamente que cada item é uma tupla de 2 elementos contendo o par índice-item correspondente). Quando todos os itens terminam, o objeto enumerate fica esgotado e a tentativa de chamar o método next() resulta em um erro StopIteration. De fato, se tentarmos agora converter esse objeto enumerate em uma lista, veremos que ela está vazia
print(list(enumerated_drinks))
Saída:
[]
Isso significa que, a cada aplicação do método next() em um objeto enumerate, um item é retirado do objeto; assim, ele vai diminuindo até se esgotar completamente. Veremos essa propriedade dos objetos enumerate também mais adiante, quando falarmos sobre aplicá-los em for-loops.
Se você quer aprender mais sobre listas em Python e outros conceitos básicos da linguagem, faça o curso Introduction to Python da DataCamp.
Enumerando uma tupla
A aplicação da função enumerate() a uma tupla é idêntica à feita em uma lista:
drinks = ('tea', 'coffee', 'cappuccino', 'lemonade')
enumerated_drinks = enumerate(drinks)
print(enumerated_drinks)
Saída:
<enumerate object at 0x000002DC3A098D40>
Mais uma vez, obtivemos um objeto enumerate cujos itens não são acessíveis com a indexação comum do Python. Vamos transformar esse objeto em outro, desta vez em um dicionário:
dict_enumerated_drinks = dict(enumerated_drinks)
print(dict_enumerated_drinks)
Saída:
{0: 'tea', 1: 'coffee', 2: 'cappuccino', 3: 'lemonade'}
Cada par chave:valor do dicionário resultante representa o par índice-item correspondente. Podemos acessar os valores do dicionário da forma Pythonic tradicional:
print(dict_enumerated_drinks[0])
print(dict_enumerated_drinks[3])
Saída:
tea
lemonade
Se você quer se aprofundar em dicionários do Python e pandas, confira o curso Intermediate Python.
Enumerando uma string
Como strings em Python são sequências de caracteres e, portanto, também objetos iteráveis, podemos criar um objeto enumerate para uma string:
enumerated_hello_world = enumerate('Hello, World!', start=10)
print(enumerated_hello_world)
Saída:
<enumerate object at 0x000002DC3A145C40>
Como fizemos antes, vamos converter esse objeto em uma lista:
list_enumerated_hello_world = list(enumerated_hello_world)
print(list_enumerated_hello_world)
Saída:
[(10, 'H'), (11, 'e'), (12, 'l'), (13, 'l'), (14, 'o'), (15, ','), (16, ' '), (17, 'W'), (18, 'o'), (19, 'r'), (20, 'l'), (21, 'd'), (22, '!')]
Vemos as já conhecidas tuplas de 2 elementos, em que o primeiro é a contagem do caractere correspondente da string (neste caso, começando em 10) e o segundo é o próprio caractere. O espaço em branco e os sinais de pontuação também são contados.
Vamos recriar o objeto enumerated_hello_world, desta vez sem start=10, e aplicar o método next nele:
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))
# Creating a list from the remaining enumerate object
print(list(enumerated_hello_world))
Saída:
(0, 'H')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')
(5, ',')
[(6, ' '), (7, 'W'), (8, 'o'), (9, 'r'), (10, 'l'), (11, 'd'), (12, '!')]
Na última linha do trecho acima, paramos de aplicar o método next() e convertemos o objeto enumerated_hello_world em uma lista. A lista resultante contém os caracteres restantes do objeto enumerate inicial após várias aplicações do método next() nele.
Enumerando com um for-loop
Por fim, vamos ver como a função enumerate() funciona em combinação com um for-loop:
for item in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(item)
Saída:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
Em cada iteração, a tupla índice-item correspondente é retornada, assim como acontece quando usamos o método next(). Mesmo que não seja óbvio pelo trecho acima, o objeto enumerate também vai perdendo seus itens (ou seja, as tuplas de 2 elementos) um a um a cada iteração. Para demonstrar isso, vamos reescrever o trecho de outra forma e conferir o comprimento da lista resultante após concluir todas as iterações:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
enumerated_drinks = enumerate(drinks)
for item in enumerated_drinks:
print(item)
print(list(enumerated_drinks))
Saída:
(0, 'tea')
(1, 'coffee')
(2, 'cappuccino')
(3, 'lemonade')
[]
Assim como com o método next(), iterar sobre um objeto enumerate vai esgotando-o gradualmente.
Vamos, porém, voltar ao trecho anterior. Como podemos acessar separadamente cada elemento de cada tupla de 2 elementos? Para isso, vamos adicionar mais uma variável de iteração e desempacotar as tuplas:
for count, drink in enumerate(['tea', 'coffee', 'cappuccino', 'lemonade']):
print(count, drink)
Saída:
0 tea
1 coffee
2 cappuccino
3 lemonade
Acima, retornamos duas variáveis para cada tupla: uma para a contagem do item e outra para o próprio item.
Note que também podemos implementar a mesma operação acima sem usar a função enumerate(). Por exemplo, podemos criar uma variável contadora fora do loop e incrementá-la em 1 a cada iteração:
count = 0
for drink in ['tea', 'coffee', 'cappuccino', 'lemonade']:
print(count, drink)
count += 1
Saída:
0 tea
1 coffee
2 cappuccino
3 lemonade
Como alternativa, podemos usar uma combinação das funções range() e len() em uma lista (ou tupla, string etc.) predefinida:
drinks = ['tea', 'coffee', 'cappuccino', 'lemonade']
for count in range(len(drinks)):
print(count, drinks[count])
Saída:
0 tea
1 coffee
2 cappuccino
3 lemonade
Além disso, em ambas as alternativas à função enumerate(), ainda podemos ajustar a variável de contagem para não começar do zero:
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])
Saída:
1 tea
2 coffee
3 cappuccino
4 lemonade
1 tea
2 coffee
3 cappuccino
4 lemonade
No entanto, em comparação com o uso da função enumerate(), ambas as abordagens acima são mais trabalhosas, mais propensas a erros (por exemplo, podemos esquecer de atualizar o contador manualmente a cada iteração) e exigem mais código. Por isso, usar enumerate() é a abordagem mais Pythonic sempre que você precisar acessar tanto a contagem quanto o item de um iterável em cada iteração.
Conclusão
Neste tutorial, vimos muitos aspectos da função enumerate() em Python, incluindo seus diversos usos, alternativas possíveis e por que ela é, no fim das contas, mais eficaz do que essas alternativas. A função enumerate() pode não ser familiar para a maioria dos iniciantes em Python e até programadores experientes podem não conhecer essa função incrivelmente útil.
Se você quiser aprender mais sobre enumerate(), faça o curso Python Data Science Toolbox (Part 2), que explora iteradores e iteráveis em detalhes e oferece exercícios interativos para praticar novas habilidades. Além disso, se você quer dominar outros conceitos de Python, vale conferir Learn Python with DataCamp, que traz diversas trilhas de habilidades e de carreira, cheat sheets, tutoriais, certificações em Python e testes de avaliação.