Generators & yield
Funções geradoras usam yield para produzir um fluxo de valores de forma preguiçosa (lazy). Elas preservam o estado local entre as etapas - ideal para arquivos grandes, sequências infinitas e pipelines ETL.
Receita
def squares(n: int):
for i in range(n):
yield i * i
for value in squares(5):
print(value)Quando usar isso:
- Lendo arquivos grandes linha por linha
- Produzindo sequências ilimitadas (IDs, tentativas)
- Encadeando estágios de map/filter sem listas
- Implementando primitivas de multitarefa cooperativa
Exemplo de Trabalho
from pathlib import Path
def read_lines(path: Path):
with path.open(encoding="utf-8") as fh:
for line in fh:
yield line.rstrip("\n")
def filter_nonempty(lines):
for line in lines:
if line.strip():
yield line
def parse_csv_rows(lines):
for line in lines:
yield line.split(",")
def pipeline(path: Path):
lines = read_lines(path)
nonempty = filter_nonempty(lines)
return parse_csv_rows(nonempty)
if __name__ == "__main__":
sample = Path("data.csv")
sample.write_text("a,b\n\n c,d\n", encoding="utf-8")
for row in pipeline(sample):
print(row)O que isso demonstra:
- Cada estágio é preguiçoso - nenhum arquivo completo na memória
- Geradores se compõem passando o iterador para a próxima função
- O manipulador de arquivo é fechado quando o gerador é coletado pelo garbage collector ou esgotado (com contexto no gerador)
- O pipeline retorna um gerador - o consumidor impulsiona a execução
Mergulho Profundo
Como Funciona
- Objeto gerador - Criado quando a função geradora é chamada - o corpo ainda não é executado.
yieldsuspende - Salva os locais do frame; retoma emnext().- StopIteration - Lançado quando o gerador retorna ou sai do final.
yield from- Delega para um sub-iterador (veja artigo dedicado).- Estado do gerador -
.gi_framecontém o ponto de execução (depuração).
Perfil de Memória
| Abordagem | Memória |
|---|---|
list(process()) | O(n) todos os itens |
| pipeline de gerador | O(1) estado por estágio |
Notas do Python
# generator expression
total = sum(x * x for x in range(1_000_000))
# fechar gerador cedo
gen = squares(100)
next(gen)
gen.close() # lança GeneratorExit internamenteArmadilhas
- Gerador esgotado - O segundo loop não faz nada. Correção: Recriar ou materializar uma vez.
- Vazamento de recursos sem
withno gerador - Se o consumidor parar cedo, o arquivo pode permanecer aberto até a coleta pelo GC. Correção:contextlib.closingoutry/finallyno gerador. - Argumento padrão mutável no gerador - O mesmo que em funções. Correção: Padrão sentinela
None. - Ordem de efeitos colaterais - Preguiçoso significa que os efeitos ocorrem no consumo, não na definição. Correção: Documentar semântica preguiçosa.
- Rastreamentos de pilha de depuração - Frames suspensos confundem novatos - use
gen.send(None)para clareza na documentação.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| List comprehension | n pequeno, precisa de reutilização | Fluxos grandes |
itertools | Operações padrão de iterador | Lógica de domínio personalizada por item |
| Gerador assíncrono | Fluxos de I/O assíncronos | Leituras de arquivo síncronas |
| Iterador de chunk do pandas | Lotes tabulares | Análise simples de linha |
FAQs
gerador vs lista?
Gerador preguiçoso de passagem única. Lista armazena todos os elementos para reutilização e indexação.
O que é yield from?
Delega a iteração para outro iterável - achata geradores aninhados eficientemente.
Geradores podem retornar valores?
return value define StopIteration.value (PEP 380) - consumido via yield from, raramente necessário sozinho.
Propósito do send()?
Comunicação estilo corrotina para dentro do gerador - avançado; asyncio usa ideias semelhantes.
Como parar a iteração cedo?
gen.close() aciona GeneratorExit no ponto de yield - limpe recursos em try/finally no gerador.
generator throw?
Injeta exceção no ponto de yield - testes e padrões de corrotina.
tipagem de Generator?
Generator[YieldType, SendType, ReturnType] para tipagem avançada - muitas vezes Iterator[T] é suficiente.
múltiplos yield em loop?
Padrão normal - cada yield produz um passo do consumidor.
desempenho vs lista?
Geradores economizam memória; lista pode ser mais rápida para sequências minúsculas devido à sobrecarga.
tee iterator?
itertools.tee divide um iterador em dois iteradores em buffer - o custo de memória aumenta.
Relacionados
- yield from & Sub-geradores - delegação
- Iteradores & o Protocolo Iterador - noções básicas do protocolo
- Gerenciadores de Contexto - limpeza em geradores
- itertools - chain, islice
Versões da Pilha: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ e uv 0.6+.