
Passo 1 de 8
Da função geradora ao objeto gerador
Diferencie a função que contém yield, o objeto gerador criado por sua chamada e o momento em que o corpo realmente começa a executar.
Trilha de aprendizado · Nível 10 · Tutorial 3
Escrever funções geradoras que entregam um valor por solicitação e preservam seu progresso entre suspensões, sem construir antecipadamente uma coleção de resultados.
Da função geradora ao objeto gerador
Diferencie a função que contém yield, o objeto gerador criado por sua chamada e o momento em que o corpo realmente começa a executar. 2 min
Entregar uma sequência com yield
Crie uma função geradora finita que calcula e entrega números sob demanda. 2 min
Rastrear a pausa e a retomada
Acompanhe como um gerador preserva sua posição e suas variáveis locais entre chamadas sucessivas de next(). 3 min
Manter percursos independentes
Veja como chamadas distintas da mesma função geradora criam percursos de consumo separados. 2 min
Reconhecer o término da geração
Entenda quando um gerador entrega seu último valor e quando ele passa a sinalizar que foi esgotado. 2 min
Observar quando os efeitos acontecem
Veja como mensagens produzidas no corpo de um gerador acompanham cada avanço, e como uma pausa deixa parte do trabalho pendente. 2 min
Localizar as falhas no avanço
Identifique quando uma validação ou falha no corpo de um gerador chega ao consumidor e diferencie esse caso do término normal. 2 min
Aplicar e verificar a produção sob demanda
Implemente uma função geradora de quadrados e verifique localmente sua produção incremental, seus estados independentes, seu esgotamento e sua validação adiada. 3 min

Passo 1 de 8
Diferencie a função que contém yield, o objeto gerador criado por sua chamada e o momento em que o corpo realmente começa a executar.
Uma função cujo corpo contém yield é uma função geradora. Ela oferece uma forma concisa de criar um iterador, sem escrever manualmente uma classe com __iter__ e __next__.
Nesta definição, primeiro_numero é a função geradora — o corpo ainda não foi executado.
def primeiro_numero(inicio):
print("Corpo iniciado")
yield inicioNão confunda definir a função, chamá-la e pedir seu próximo valor: são operações distintas.

yield caracteriza a função; next(...) é que começa a executar seu corpo.
gerador = primeiro_numero(10)
print("Gerador criado")
valor = next(gerador)
print(valor)A chamada primeiro_numero(10) cria um objeto gerador e o associa a gerador. Ela não imprime "Corpo iniciado".
A primeira chamada de next(gerador) inicia o corpo até alcançar yield. Portanto, a saída é:
Gerador criado
Corpo iniciado
10O objeto gerador já é um iterador: funciona com iter, next e for, sem que você implemente esses métodos.
Dica
O corpo da função é adiado, mas os argumentos da chamada são avaliados imediatamente. Em primeiro_numero(calcular_inicio()), calcular_inicio() roda ao criar o gerador; já o corpo de primeiro_numero só começa no primeiro consumo.
Considere:
def uma_vez(valor):
print("executando")
yield valor
fluxo = uma_vez(7)Qual afirmação está correta logo após a última linha?

Passo 2 de 8
Crie uma função geradora finita que calcula e entrega números sob demanda.
Dentro de uma função geradora, yield calcula ou obtém um valor, entrega esse valor a quem consome o gerador e pausa a execução. Em um laço, isso permite produzir vários resultados: cada passagem alcança um novo yield.
Diferentemente de criar uma lista com todos os resultados antes, a função abaixo calcula cada dobro quando esse valor é solicitado.
def dobrados(quantidade):
for numero in range(quantidade):
yield numero * 2
valores = dobrados(3)
print(next(valores)) # 0
print(next(valores)) # 2
print(next(valores)) # 4
O laço calcula um resultado e o entrega; os demais resultados permanecem para solicitações posteriores.
yield entrega um valor sem encerrar a função geradora, então o laço pode seguir para outra iteração quando houver uma nova solicitação.
Já return encerra a execução naquele ponto. Se ele substituir yield no corpo do laço, nenhum valor será entregue por essa instrução e não haverá próxima passagem do laço nessa execução.
Exemplo
def com_yield():
for numero in range(3):
yield numero
# Produz: 0, depois 1, depois 2
def com_return():
for numero in range(3):
return numero
# Encerra na primeira passagem; não produz elementos.Complete a função para entregar os quadrados de 0 até limite - 1, um por solicitação:
def quadrados(limite):
for numero in range(limite):
______ numero * numeroNo seu editor, crie a função quadrados do exercício. Em seguida, crie gerador = quadrados(4) e execute quatro chamadas a next(gerador). Você deve observar, em ordem: 0, 1, 4 e 9.
A função não monta uma lista de quadrados: o cálculo de cada expressão ocorre durante o consumo, quando a execução alcança seu respectivo yield.
Por que trocar yield por return em quadrados impediria a produção dos valores seguintes?
Escreva pelo menos 40 caracteres (0/40).

Passo 3 de 8
Acompanhe como um gerador preserva sua posição e suas variáveis locais entre chamadas sucessivas de next().
Quando next() avança um gerador, o corpo executa até alcançar o próximo yield. Nesse ponto, o valor é entregue e a execução fica suspensa.
As variáveis locais continuam guardadas no objeto gerador. Na próxima chamada de next(), o Python retoma logo depois do yield que entregou o valor anterior — não no início da função.
Execute uma chamada de cada vez e observe a posição em que o corpo para.
def pares_ate(limite):
atual = 0
while atual < limite:
yield atual
atual += 2
numeros = pares_ate(6)
print(next(numeros)) # 0
print(next(numeros)) # 2Dica
Logo após o primeiro next(numeros), a variável local atual ainda vale 0. A instrução atual += 2 está pendente: ela só será executada quando houver outro avanço do gerador.
O diagrama mostra que cada chamada de next() retoma o mesmo percurso no ponto onde ele havia parado.

Após cada entrega, o contador mantém o valor usado no yield; a atualização posterior só ocorre na retomada.
Exemplo
next(numeros): executa atual = 0, entra no while e entrega 0. Pausa no yield; atual continua sendo 0.next(numeros): retoma após o yield, executa atual += 2, volta ao teste do laço e entrega 2. Pausa novamente; agora atual vale 2.O valor entregue e a próxima instrução pendente são partes diferentes do estado do gerador.
Considere o gerador pares_ate(6) e as duas primeiras chamadas de next(numeros). Coloque os acontecimentos na ordem em que ocorrem.

Passo 4 de 8
Veja como chamadas distintas da mesma função geradora criam percursos de consumo separados.
Uma função geradora pode ser chamada várias vezes. Cada chamada cria um novo objeto gerador, com suas próprias variáveis locais e seu próprio ponto de suspensão.
Assim, pares(0) e pares(10) executam o mesmo código, mas não compartilham contador nem progresso.
def pares(inicio):
numero = inicio
while True:
yield numero
numero += 2
primeiros = pares(0)
dez_em_diante = pares(10)

Após as chamadas, os dois objetos existem, mas nenhum corpo foi avançado ainda.
Agora, as chamadas a next são intercaladas. Cada uma retoma apenas o objeto recebido como argumento.
print(next(primeiros)) # 0
print(next(dez_em_diante)) # 10
print(next(primeiros)) # 2
print(next(primeiros)) # 4
print(next(dez_em_diante)) # 12
primeiros avançou três vezes; dez_em_diante, duas. Cada contador local conserva seu próprio valor.
Depois do código anterior, associe cada expressão ao resultado que ela produzirá.
Toque em um item e depois no par correspondente.

Passo 5 de 8
Entenda quando um gerador entrega seu último valor e quando ele passa a sinalizar que foi esgotado.
Um yield entrega um elemento e suspende a função. O término normal só é percebido quando uma chamada posterior de next() retoma o corpo e ele chega ao fim — ou encontra um return sem valor.
Por isso, receber o último valor não significa que o consumidor já observou StopIteration: em geral, é preciso pedir mais um valor.
A imagem separa a entrega final do avanço que confirma o esgotamento.

O segundo avanço não entrega None; ele informa que não há outro elemento.
Execute cada bloco separadamente para observar os avanços.
def por_fim_do_corpo():
yield "único valor"
def por_return():
yield "único valor"
return
for fabrica in (por_fim_do_corpo, por_return):
gerador = fabrica()
print(next(gerador))
print(next(gerador, "ESGOTADO"))Nos dois casos, a saída é único valor e depois ESGOTADO. O return sem valor encerra a função; ele não produz None como próximo elemento.
O argumento padrão de next permite observar o esgotamento sem deixar StopIteration interromper o programa.
Dica
Depois de esgotado, o mesmo objeto gerador continua esgotado. Novos next(gerador, "ESGOTADO") retornam o padrão; ele não volta ao início. Para um novo percurso, chame a função geradora outra vez.
Mesmo com yield escrito na função, este caminho pode terminar antes de alcançá-lo.
def codigos_positivos(limite):
if limite <= 0:
return
for codigo in range(limite):
yield codigo
vazio = codigos_positivos(0)
print(next(vazio, "SEM VALORES"))
print(next(vazio, "AINDA ESGOTADO"))A chamada cria o objeto normalmente. Porém, no primeiro avanço, a condição é atendida e o return encerra o corpo antes de qualquer yield. Assim, a geração é vazia.
Considere:
def pares():
yield 0
yield 2
g = pares()
next(g) # entrega 0A próxima chamada next(g) lança StopIteration.
Qual é o resultado de next(g, "fim")?
def talvez_valor(ativo):
if not ativo:
return
yield "disponível"
g = talvez_valor(False)
Passo 6 de 8
Veja como mensagens produzidas no corpo de um gerador acompanham cada avanço, e como uma pausa deixa parte do trabalho pendente.
Um print dentro de uma função geradora só acontece quando a execução chega àquela linha. Portanto, criar o objeto com etapas() não mostra nenhuma mensagem do corpo: o primeiro next() é que inicia a execução.
Depois de um yield, o gerador fica pausado. As linhas abaixo dele continuam pendentes até uma nova solicitação de valor.
Execute este exemplo no seu Python 3.
def etapas():
print("início do corpo")
yield "primeiro"
print("entre os valores")
yield "segundo"
print("fim do corpo")
fluxo = etapas()
print("gerador criado")
print(next(fluxo))
print(next(fluxo))A execução avança somente até o próximo ponto de suspensão.

Cada next() retoma o corpo a partir da pausa anterior e pode alcançar novos efeitos.
Se você executar apenas next(fluxo) uma vez, verá início do corpo e receberá primeiro. Nesse ponto, o gerador está suspenso no primeiro yield.
Assim, entre os valores e fim do corpo ainda não foram impressos. Elas são instruções pendentes, não mensagens já produzidas.
Considere o código da tela anterior e duas chamadas a next(fluxo). Coloque os acontecimentos na ordem em que ocorrem.
No código anterior, deixe somente estas linhas depois de fluxo = etapas():
print("gerador criado")
print(next(fluxo))Execute o arquivo. Interrompa aí: não faça o segundo next(). Compare a saída com o corpo da função e identifique quais print continuam pendentes.
Após executar apenas o primeiro next(fluxo), quais mensagens do corpo apareceram? Quais efeitos ainda não ocorreram e por quê?
Escreva pelo menos 40 caracteres (0/40).

Passo 7 de 8
Identifique quando uma validação ou falha no corpo de um gerador chega ao consumidor e diferencie esse caso do término normal.
Uma chamada a uma função geradora cria o objeto gerador, mas não percorre seu corpo. Portanto, uma validação escrita no corpo só ocorre quando algum consumo — como next() — alcança essa linha.
Se a validação vier antes do primeiro yield, a exceção aparecerá no primeiro avanço, e não na chamada que criou o gerador.
def contar_ate(limite):
if limite < 0:
raise ValueError("limite não pode ser negativo")
for numero in range(limite):
yield numero
numeros = contar_ate(-2) # cria o gerador; ainda não há erro
next(numeros) # ValueError: limite não pode ser negativo
A condição é avaliada no primeiro avanço porque é nesse momento que o corpo começa a executar.
Uma exceção pode ocorrer depois de valores já entregues. Nesse caso, os avanços anteriores funcionam; a chamada de next() que alcança a falha recebe a exceção.
Quando uma exceção do corpo não é tratada, ela se propaga ao consumidor e aquele objeto gerador fica encerrado. Diferentemente do término normal, não é um sinal de que a sequência terminou como planejado.
def leituras():
yield "primeira leitura"
raise ValueError("leitura inválida")
yield "esta linha não será alcançada"
fluxo = leituras()
print(next(fluxo)) # primeira leitura
print(next(fluxo)) # ValueError: leitura inválida
# O gerador foi encerrado pela falha.Atenção
Dentro de uma função geradora, não use raise StopIteration para indicar término. Se esse StopIteration escapar do corpo — inclusive vindo de uma chamada interna — o Python o apresenta ao consumidor como RuntimeError.
Para terminar normalmente, deixe o corpo chegar ao fim ou use return sem valor. Assim, o consumidor observará o término normal como StopIteration ao tentar avançar além dos valores produzidos.
Associe cada situação ao que o consumidor observa.
Toque em um item e depois no par correspondente.

Passo 8 de 8
Implemente uma função geradora de quadrados e verifique localmente sua produção incremental, seus estados independentes, seu esgotamento e sua validação adiada.
No seu computador, crie um arquivo Python e implemente quadrados_ate(limite). Ela deve produzir os quadrados de 0 até limite - 1, um por vez, sem montar uma lista. Para tornar a execução observável, mantenha as mensagens antes e depois de cada yield.
A validação do limite fica no corpo da função: por isso, um limite negativo só gera erro quando o objeto é avançado pela primeira vez.
def quadrados_ate(limite):
if limite < 0:
raise ValueError("limite não pode ser negativo")
for numero in range(limite):
print(f"calculando {numero}")
yield numero ** 2
print(f"retomando após {numero}")
Cada chamada cria seu próprio percurso: avançar A não altera a posição de B.
Execute este roteiro completo. Leia as mensagens junto com os valores: elas revelam quais trechos do corpo foram realmente alcançados. Observe especialmente que criar negativo não falha; a falha aparece no next(negativo).
a = quadrados_ate(3)
b = quadrados_ate(2)
print("A, primeiro:", next(a))
print("B, primeiro:", next(b))
print("A, segundo:", next(a))
print("A, terceiro:", next(a))
try:
next(a)
except StopIteration:
print("A está esgotado")
vazio = quadrados_ate(0)
try:
next(vazio)
except StopIteration:
print("Gerador vazio: nenhum valor")
negativo = quadrados_ate(-1)
try:
next(negativo)
except ValueError as erro:
print("Erro no avanço:", erro)Dica
a entrega 0, 1 e 4; b começa novamente em 0, pois tem estado próprio. Depois da última entrega de a, mais um avanço executa a mensagem de retomada pendente e encontra o fim do corpo, causando StopIteration. Um limite zero termina sem entrega; um limite negativo lança ValueError no primeiro avanço.
Depois de executar o roteiro, relate: quais valores a e b entregaram; o que acontece ao retomar a depois do primeiro valor; e em qual momento ocorrem o esgotamento, o caso vazio e o ValueError.
Escreva pelo menos 180 caracteres (0/180).
Resumo
yield cria uma função geradora; chamá-la cria um objeto gerador, mas não executa ainda o corpo.next faz o objeto executar até o próximo yield, entrega o valor e preserva a posição e as variáveis locais.yield, é observado como StopIteration; uma validação no corpo ocorre no avanço que a alcança.Parabéns! Você concluiu: Produzir valores sob demanda com yield
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks