Os geradores em Python são um mecanismo poderoso para trabalhar eficientemente com sequências de dados. Eles permitem criar objetos iteráveis que calculam valores sob demanda, economizando significativamente a memória do sistema. A base dos geradores é a palavra-chave yield, que é fundamentalmente diferente do tradicional return.
Fundamentos dos Geradores Python
Geradores são funções especiais que retornam iteradores em vez de valores específicos. A principal diferença está no uso da palavra-chave yield, que salva o estado da função entre as chamadas.
A cada chamada a uma função geradora, a execução é retomada de onde parou no último yield. Isso torna os geradores a solução ideal para processar grandes fluxos de dados sem precisar carregar tudo na memória.
def gerador_simples():
yield 1
yield 2
yield 3
gen = gerador_simples()
for valor in gen:
print(valor)
Resultado da execução:
1
2
3
Como funciona a palavra-chave yield
A palavra-chave yield funciona com o princípio de "lazy evaluation" (avaliação preguiçosa):
- Na primeira chamada
next(), a execução começa do início da função até o primeiroyield - Após retornar o valor, a função "congela" seu estado
- A próxima chamada
next()retoma a execução de onde parou - O processo continua até a função terminar
def contador():
print("Início do gerador")
yield 1
print("Após o primeiro yield")
yield 2
print("Após o segundo yield")
gen = contador()
next(gen) # Saída: Início do gerador
next(gen) # Saída: Após o primeiro yield
Vantagens dos Geradores sobre Listas
Usar geradores em vez de listas traz vantagens significativas ao trabalhar com grandes volumes de dados:
Economia de memória: Geradores não armazenam todos os valores de uma vez, criando-os conforme necessário.
# Lista - ocupa muita memória
lista_quadrados = [x * x for x in range(1000000)]
# Gerador - consumo mínimo de memória
gen_quadrados = (x * x for x in range(1000000))
Performance: Geradores começam a retornar valores imediatamente, sem esperar a criação completa da sequência.
Criando Sequências Infinitas
Geradores permitem criar sequências infinitas sem risco de estouro de memória:
def contador_infinito(inicio=0):
while True:
yield inicio
inicio += 1
contador = contador_infinito()
print(next(contador)) # 0
print(next(contador)) # 1
print(next(contador)) # 2
Métodos para Trabalhar com Geradores
Geradores suportam vários métodos especializados:
next() — obtém o próximo valor do gerador
send(valor) — envia um valor para dentro do gerador
throw() — gera uma exceção dentro do gerador
close() — para o gerador forçadamente
def gerador_interativo():
valor = yield "Início"
while True:
valor = yield f"Recebido: {valor}"
gen = gerador_interativo()
print(next(gen)) # Início
print(gen.send(42)) # Recebido: 42
print(gen.send("Olá")) # Recebido: Olá
Tratamento de Exceções em Geradores
Geradores suportam mecanismos de tratamento de exceções, incluindo a exceção especial GeneratorExit:
def gerador_exemplo():
try:
yield 1
yield 2
except GeneratorExit:
print("Gerador fechado!")
gen = gerador_exemplo()
print(next(gen)) # 1
gen.close() # Saída: Gerador fechado!