Python: Iteradores e Geradores

Última atualização: 2026-09-22

Você tem usado for x in lista: para iterar dados, mas já pensou no que pode seguir in? Por que range(1000000) não ocupa memória? Iteradores e geradores são a resposta. Eles permitem lidar com fluxos de dados "infinitos" — gerados sob demanda, não armazenados na memória.


Funções internas relacionadas:reversed() iter() next() return yield for

1. O que é o Protocolo de Iterador

Em Python, qualquer objeto que pode ser usado em um laço for é iterável. Eles implementam o protocolo de iterador:

▶ Exemplo: Básico do Protocolo de Iterador

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Executed successfully

Um laço for essencialmente faz isto:

▶ Exemplo: Simulando um Laço for Manualmente

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Executed successfully


2. Iteradores Personalizados

Implemente __iter__ e __next__ para fazer sua classe funcionar com laços for:

▶ Exemplo: Iterador de Contagem Regressiva

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Function defined successfully

▶ Exemplo: Iterador de Fibonacci (Dificuldade ⭐⭐)

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Function defined successfully


3. Geradores: yield

Escrever uma classe com __iter__ e __next__ é tedioso. Funções geradoras usam yield para alcançar o mesmo resultado:

▶ Exemplo: Gerador de Contagem Regressiva

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Function defined successfully

yield é diferente de return — return encerra uma função, enquanto yield pausa ela, lembra o estado atual e retoma de onde parou na próxima chamada.

▶ Exemplo: Gerador de Fibonacci

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Function defined successfully
💡 Geradores vs Classes Iteradoras: Funções geradoras (yield) são geralmente muito mais concisas que escrever classes iteradoras manualmente. Na maioria dos cenários, geradores são a primeira escolha. Considere escrever uma classe iteradora apenas se precisar manter estado complexo ou precisar de métodos adicionais.



4. Carregamento Preguiçoso com Geradores

A maior vantagem dos geradores — eles não geram todos os dados de uma vez, mas sob demanda:

▶ Exemplo: Carregamento Preguiçoso de Gerador

▶ Experimente

Saída:

TEXT 📖 Somente leitura
Gerando 1
Gerando 2
Gerando 3
Gerador criado, mas ainda não executado

Vê o fluxo de execução? Cada chamada a next() executa até o próximo yield, depois pausa. Isso é incrivelmente útil para grandes dados:

▶ Exemplo: Lendo Grandes Arquivos Linha por Linha

TEXT 📖 Somente leitura
# Forma "preguiçosa" de processar grandes arquivos
def read_large_file(filename):
    """Lê um grande arquivo linha por linha, sem carregar tudo na memória"""
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

# Processa com gerador — apenas uma linha na memória por vez
for line in read_large_file("huge_log.txt"):
    if "ERRO" in line:
        print(line)  # Velocidade limitada apenas pela velocidade de leitura do disco


5. Expressões Geradoras

Similar a list comprehensions, mas com parênteses — expressões geradoras são avaliadas preguiçosamente:

▶ Exemplo: Expressão Geradora

▶ Experimente

Saída:

TEXT 📖 Somente leitura
# Executed successfully
Comparação List Comprehension [] Expressão Geradora ()
Memória Todos os dados na memória Sob demanda, quase nenhuma
Velocidade Geração e acesso rápidos Preguiçosa, computa cada vez
Reutilizável Pode iterar várias vezes Só pode iterar uma vez
Caso de uso Dados pequenos, uso repetido Grandes dados, passagem única
💡 Quando usar geradores? ① Grandes conjuntos de dados (milhares+ itens). ② Só precisa iterar uma vez. ③ Precisa de processamento em fluxo (arquivo linha por linha, fluxos de dados em tempo real). ④ Quer expressar "sequências infinitas."



6. Casos de Uso Comuns


Funções internas relacionadas:reversed() iter() next() return yield for

❓ Perguntas Frequentes

P Qual a diferença entre um gerador e uma função regular?
R Uma função regular usa return para retornar um resultado uma vez e termina. Um gerador usa yield para produzir valores um de cada vez, pausando e salvando o estado após cada yield. Cada chamada a uma função geradora retorna um novo objeto gerador.
P Geradores só podem ser iterados uma vez. E se eu precisar iterar várias vezes?
R Use list(gen) para converter o gerador em uma lista. Mas se os dados forem muito grandes, converter para lista esgotará a memória. Um compromisso: use itertools.tee() para copiar o gerador, ou chame a função geradora novamente para criar um novo.
P yield e return podem coexistir?
R Sim — return em um gerador efetivamente para a iteração e levanta StopIteration com um valor opcional. Mas é raramente usado. Se você encontrar uma função que precisa tanto de yield quanto de return, repense o design.

📖 Resumo



📝 Exercícios

  1. Básico (Dificuldade ⭐): Escreva um gerador even_numbers(max_n) que gera todos os números pares de 0 até max_n.

  2. Intermediário (Dificuldade ⭐⭐): Escreva um gerador repeat_list(items, times) que repete cada elemento de uma lista times vezes. Por exemplo, list(repeat_list([1, 2, 3], 2)) retorna [1, 1, 2, 2, 3, 3].

  3. Desafio (Dificuldade ⭐⭐⭐): Escreva um gerador deep_flatten(nested) que achata listas arbitrariamente aninhadas. Por exemplo, list(deep_flatten([1, [2, [3, 4]], 5])) retorna [1, 2, 3, 4, 5]. Dica: Itere sobre os elementos; se um elemento for uma lista, gere recursivamente a partir dele com yield from.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%