Skip to content
Tópicos
Python
Python Enumerate: Loop with Index the Right Way

Python Enumerate: Loop com índice da maneira correta

Updated on

Rastrear índices ao percorrer sequências Python é um problema comum que os desenvolvedores enfrentam diariamente. A abordagem típica de usar range(len(list)) funciona, mas cria código verboso que obscurece sua intenção e introduz oportunidades desnecessárias para erros de deslocamento. Quando você precisa tanto do elemento quanto de sua posição, gerenciar manualmente uma variável contador adiciona sobrecarga cognitiva e torna seu código mais difícil de ler.

A função embutida enumerate() do Python resolve este problema elegantemente. Ela envolve qualquer iterável e retorna pares de índices e valores, eliminando o gerenciamento manual de contadores enquanto torna seu código mais pythônico e legível. Este guia mostra como usar enumerate() efetivamente, desde padrões básicos até técnicas avançadas nas quais desenvolvedores Python profissionais confiam.

📚

O que é Python Enumerate?

A função enumerate() é uma ferramenta embutida do Python que adiciona um contador a um iterável e o retorna como um objeto enumerate. Este objeto produz pares de tuplas (índice, valor) à medida que você itera através dele.

fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
 
# Output:
# 0: apple
# 1: banana
# 2: cherry

A assinatura da função é enumerate(iterable, start=0), onde iterable é qualquer sequência ou iterador, e start é o valor de índice inicial opcional.

Sintaxe e uso básico do Enumerate

Loop simples com índice

Em vez de usar o padrão range(len()), enumerate() fornece acesso direto ao índice e ao valor:

# Without enumerate (less Pythonic)
colors = ['red', 'green', 'blue']
for i in range(len(colors)):
    print(f"Color {i} is {colors[i]}")
 
# With enumerate (Pythonic)
for i, color in enumerate(colors):
    print(f"Color {i} is {color}")

Parâmetro de início personalizado

O parâmetro start permite começar a contar a partir de qualquer número:

tasks = ['Review code', 'Write tests', 'Deploy']
for num, task in enumerate(tasks, start=1):
    print(f"Task #{num}: {task}")
 
# Output:
# Task #1: Review code
# Task #2: Write tests
# Task #3: Deploy

Isso é particularmente útil ao exibir listas numeradas para os usuários, onde a indexação baseada em 1 é mais natural do que a baseada em 0.

Enumerate com diferentes iteráveis

Listas e tuplas

enumerate() funciona perfeitamente com listas e tuplas:

coordinates = [(10, 20), (30, 40), (50, 60)]
for idx, (x, y) in enumerate(coordinates):
    print(f"Point {idx}: x={x}, y={y}")

Strings

Strings são iteráveis, então enumerate() as manipula caractere por caractere:

word = "Python"
for position, char in enumerate(word):
    print(f"Character at position {position}: {char}")
 
# Output:
# Character at position 0: P
# Character at position 1: y
# Character at position 2: t
# ...

Dicionários

Ao enumerar dicionários, você itera sobre as chaves por padrão:

config = {'host': 'localhost', 'port': 8080, 'debug': True}
 
# Enumerate keys
for i, key in enumerate(config):
    print(f"{i}: {key}")
 
# Enumerate key-value pairs
for i, (key, value) in enumerate(config.items()):
    print(f"{i}: {key} = {value}")

Objetos de arquivo

enumerate() é especialmente útil ao processar arquivos linha por linha:

with open('data.txt', 'r') as file:
    for line_num, line in enumerate(file, start=1):
        if 'ERROR' in line:
            print(f"Error found on line {line_num}: {line.strip()}")

Comparação: Enumerate vs outros métodos de rastreamento de índice

MétodoLegibilidadeDesempenhoMemóriaCaso de uso
enumerate(list)AltaRápidoBaixa (iterador preguiçoso)Quando você precisa de índice e valor
range(len(list))BaixaRápidoBaixaCódigo legado (evitar)
zip(range(), list)MédiaRápidoBaixaAo combinar múltiplos iteráveis
Contador manualBaixaRápidoBaixaNunca use (propenso a erros)
data = ['a', 'b', 'c']
 
# Method 1: enumerate (recomendado)
for i, item in enumerate(data):
    print(i, item)
 
# Method 2: range(len()) (não pythônico)
for i in range(len(data)):
    print(i, data[i])
 
# Method 3: zip with range (excessivamente complexo)
for i, item in zip(range(len(data)), data):
    print(i, item)
 
# Method 4: manual counter (propenso a erros)
counter = 0
for item in data:
    print(counter, item)
    counter += 1

A abordagem enumerate() vence em legibilidade e estilo pythônico enquanto mantém excelente desempenho.

Padrões avançados de Enumerate

Enumerate com compreensões de lista

Combine enumerate() com compreensões de lista para transformações concisas:

numbers = [10, 20, 30, 40]
 
# Add index to each element
indexed = [(i, n) for i, n in enumerate(numbers)]
# Result: [(0, 10), (1, 20), (2, 30), (3, 40)]
 
# Filter based on index
even_positions = [n for i, n in enumerate(numbers) if i % 2 == 0]
# Result: [10, 30]
 
# Transform with index
multiplied = [n * i for i, n in enumerate(numbers, start=1)]
# Result: [10, 40, 90, 160]

Enumerate com Zip

Combine enumerate() e zip() para iterar sobre múltiplas sequências com índices:

names = ['Alice', 'Bob', 'Charlie']
scores = [85, 92, 78]
 
for rank, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"#{rank}: {name} scored {score}")
 
# Output:
# #1: Alice scored 85
# #2: Bob scored 92
# #3: Charlie scored 78

Enumerate em ordem reversa

Para enumerar em ordem reversa, combine enumerate() com reversed():

items = ['first', 'second', 'third']
 
# Reverse the items, but indices still go 0, 1, 2
for i, item in enumerate(reversed(items)):
    print(f"{i}: {item}")
 
# Output:
# 0: third
# 1: second
# 2: first
 
# If you want descending indices, calculate them manually
length = len(items)
for i, item in enumerate(reversed(items)):
    actual_index = length - 1 - i
    print(f"{actual_index}: {item}")
 
# Output:
# 2: third
# 1: second
# 0: first

Enumerate aninhado

Use chamadas enumerate() aninhadas para dados multidimensionais:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
 
for row_idx, row in enumerate(matrix):
    for col_idx, value in enumerate(row):
        print(f"matrix[{row_idx}][{col_idx}] = {value}")

Este padrão é valioso para algoritmos baseados em grade, tabuleiros de jogos ou qualquer estrutura aninhada onde você precise de coordenadas.

Desempenho e eficiência de memória

A função enumerate() retorna um iterador, não uma lista. Isso significa que ela gera pares índice-valor sob demanda em vez de criá-los todos antecipadamente na memória.

# enumerate returns an iterator
large_list = range(1_000_000)
enum_obj = enumerate(large_list)
print(type(enum_obj))  # <class 'enumerate'>
 
# Memory efficient - doesn't create a million tuples at once
for i, value in enum_obj:
    if i > 5:
        break
    print(i, value)

Comparação de benchmark

import timeit
 
data = list(range(10_000))
 
# Time enumerate
time_enum = timeit.timeit(
    'for i, x in enumerate(data): pass',
    globals={'data': data},
    number=1000
)
 
# Time range(len())
time_range = timeit.timeit(
    'for i in range(len(data)): x = data[i]',
    globals={'data': data},
    number=1000
)
 
print(f"enumerate: {time_enum:.4f}s")
print(f"range(len): {time_range:.4f}s")
# enumerate is typically 10-20% faster due to fewer lookups

A diferença de desempenho vem do fato de enumerate() evitar operações de indexação repetidas. Ao usar data[i], o Python deve realizar uma busca para cada elemento, enquanto enumerate() produz valores diretamente.

Erros comuns a evitar

Erro 1: Modificar o comprimento da sequência enquanto enumera

# Wrong - causes unexpected behavior
items = [1, 2, 3, 4, 5]
for i, item in enumerate(items):
    if item % 2 == 0:
        items.remove(item)  # Modifies list during iteration
 
# Correct - collect indices first
items = [1, 2, 3, 4, 5]
to_remove = [i for i, item in enumerate(items) if item % 2 == 0]
for i in reversed(to_remove):
    items.pop(i)

Erro 2: Desempacotar sem tupla

# Wrong - tries to unpack the enumerate object itself
for item in enumerate(['a', 'b']):
    print(item)  # Prints: (0, 'a'), (1, 'b')
 
# Correct - unpack each tuple
for i, item in enumerate(['a', 'b']):
    print(i, item)

Erro 3: Converter para lista desnecessariamente

# Wrong - wastes memory
for i, item in list(enumerate(huge_dataset)):
    process(i, item)
 
# Correct - keep it as iterator
for i, item in enumerate(huge_dataset):
    process(i, item)

Erro 4: Usar Enumerate quando você não precisa do índice

# Wrong - unnecessary complexity
for i, item in enumerate(items):
    print(item)  # Never uses i
 
# Correct - simple iteration
for item in items:
    print(item)

Erro 5: Ignorar o parâmetro Start para exibição ao usuário

# Wrong - users see 0-based indexing
results = ['first', 'second', 'third']
for i, result in enumerate(results):
    print(f"{i}. {result}")  # 0. first, 1. second...
 
# Correct - users see natural numbering
for i, result in enumerate(results, start=1):
    print(f"{i}. {result}")  # 1. first, 2. second...

Por baixo dos panos: Como o Enumerate funciona

O enumerate() do Python é implementado como uma classe de iterador. Aqui está uma versão simplificada de como funciona internamente:

class Enumerate:
    def __init__(self, iterable, start=0):
        self.iterable = iter(iterable)
        self.count = start
 
    def __iter__(self):
        return self
 
    def __next__(self):
        value = next(self.iterable)
        result = (self.count, value)
        self.count += 1
        return result
 
# Using our implementation
items = ['x', 'y', 'z']
for i, item in Enumerate(items):
    print(i, item)

Esta implementação revela por que enumerate() é eficiente em memória. Ela não pré-computa todos os pares índice-valor; em vez disso, mantém um contador e gera pares em cada iteração.

A implementação real do CPython é otimizada em C para máximo desempenho, mas segue o mesmo protocolo de iterador.

Casos de uso do mundo real

Processando arquivos CSV com números de linha

import csv
 
with open('sales.csv', 'r') as file:
    reader = csv.DictReader(file)
    for row_num, row in enumerate(reader, start=2):  # start=2 accounts for header
        try:
            amount = float(row['amount'])
            if amount < 0:
                print(f"Warning: Negative amount on row {row_num}")
        except ValueError:
            print(f"Error: Invalid amount on row {row_num}: {row['amount']}")

Construindo listas ordenadas HTML

def create_html_list(items):
    html = "<ol>\n"
    for i, item in enumerate(items, start=1):
        html += f'  <li id="item-{i}">{item}</li>\n'
    html += "</ol>"
    return html
 
tasks = ["Write code", "Review PR", "Deploy"]
print(create_html_list(tasks))

Rastreando o progresso no processamento de dados

def process_dataset(data, batch_size=100):
    total = len(data)
    for i, record in enumerate(data):
        process_record(record)
 
        # Show progress every batch_size items
        if (i + 1) % batch_size == 0:
            progress = (i + 1) / total * 100
            print(f"Progress: {progress:.1f}% ({i + 1}/{total})")

Ao trabalhar com processamento de dados em notebooks Jupyter, rastrear índices torna-se ainda mais valioso para depuração. Ferramentas como RunCell (opens in a new tab) ajudam cientistas de dados a depurar loops enumerados fornecendo análise alimentada por IA de seus padrões de iteração e estados de variáveis em cada etapa.

Encontrando todas as ocorrências em texto

def find_all_positions(text, substring):
    positions = [i for i, char in enumerate(text) if text[i:i+len(substring)] == substring]
    return positions
 
text = "Python is powerful. Python is popular. Python is everywhere."
positions = find_all_positions(text, "Python")
print(f"'Python' found at positions: {positions}")
# Output: 'Python' found at positions: [0, 20, 40]

FAQ

O que o enumerate faz em Python?

A função enumerate() adiciona um contador a qualquer objeto iterável e retorna um objeto enumerate que produz pares de tuplas (índice, valor). Ela elimina a necessidade de rastrear índices manualmente ao percorrer sequências, tornando o código mais legível e menos propenso a erros. A função aceita um parâmetro start opcional para começar a contar a partir de qualquer número em vez do padrão 0.

Como o enumerate é diferente do range?

A função enumerate() funciona com qualquer iterável e retorna tanto o índice quanto o valor real do elemento, enquanto range() apenas gera números que você deve usar para indexar a sequência manualmente. Usar enumerate(items) é mais pythônico e legível do que range(len(items)) porque evita operações de indexação redundantes e expressa claramente a intenção de iterar com índices.

Você pode usar enumerate com dicionários?

Sim, enumerate() funciona com dicionários. Quando você enumera um dicionário diretamente, ele opera nas chaves. Para enumerar pares chave-valor, use enumerate(dict.items()) que lhe dá um índice mais a tupla (chave, valor). Este padrão é útil quando você precisa rastrear a posição das entradas do dicionário durante a iteração.

O enumerate é mais rápido do que usar range(len())?

Sim, enumerate() é tipicamente 10-20% mais rápido do que range(len()) porque evita operações repetidas de busca de índice. Quando você usa data[i] dentro de um loop, o Python realiza uma busca para cada elemento, enquanto enumerate() produz valores diretamente do iterador. A diferença de desempenho torna-se mais perceptível com conjuntos de dados maiores e é mais significativa quando combinada com a legibilidade superior do enumerate.

O enumerate cria uma lista na memória?

Não, enumerate() retorna um iterador preguiçoso que gera pares índice-valor sob demanda em vez de criá-los todos de uma vez na memória. Isso o torna eficiente em memória mesmo com conjuntos de dados muito grandes. Cada tupla é criada apenas quando solicitada durante a iteração, então enumerar uma lista de um milhão de elementos não cria um milhão de tuplas antecipadamente. Se você precisar de uma lista real de tuplas, deve convertê-la explicitamente com list(enumerate(data)).

Conclusão

A função enumerate() é uma ferramenta fundamental no kit de ferramentas de todo desenvolvedor Python. Ela transforma padrões verbosos de rastreamento de índice em código limpo e legível que comunica claramente a intenção. Ao retornar iteradores preguiçosos de pares índice-valor, enumerate fornece tanto desempenho quanto eficiência de memória enquanto elimina erros comuns associados ao gerenciamento manual de contadores.

Comece a usar enumerate() sempre que precisar tanto de posição quanto de valor em seus loops. Adote o parâmetro start para numeração voltada ao usuário, combine-o com zip() para iteração de múltiplas sequências e aproveite-o em compreensões de lista para transformações concisas. Esses padrões tornarão seu código Python mais pythônico, mantível e profissional.

📚