
Passo 1 de 8
Transformar valores com map
Aplique uma função nomeada aos valores de uma fonte somente quando cada resultado for solicitado.
Trilha de aprendizado · Nível 10 · Tutorial 7
Conectar fontes, filtros, transformações e limites em um fluxo incremental, usando funções e itertools sem criar listas intermediárias desnecessárias.
Transformar valores com map
Aplique uma função nomeada aos valores de uma fonte somente quando cada resultado for solicitado. 2 min
Selecionar valores com filter
Selecione valores sob demanda com um predicado e acompanhe os candidatos examinados até cada resultado aceito. 2 min
Encadear fontes com chain
Una fontes em uma única sequência de consumo, preservando a ordem e sem criar uma lista concatenada. 2 min
Organizar etapas e observar a demanda
Nomeie cada etapa de um fluxo e use registros para observar o que acontece na montagem e no consumo. 3 min
Decidir quando filtrar e transformar
Compare a ordem de filter e map para preservar o critério de seleção e evitar trabalho desnecessário quando isso for válido. 3 min
Posicionar o limite no fluxo
Compare o limite aplicado aos candidatos com o limite aplicado aos resultados aceitos e observe o consumo da fonte. 3 min
Reconhecer etapas que consomem toda a entrada
Entenda por que sorted interrompe a entrega incremental e como sua posição em relação a islice altera tanto o consumo quanto o significado do resultado. 2 min
Aplicação final: compor e verificar um fluxo
Monte um fluxo completo, execute testes locais e use os registros para comprovar que o processamento só acontece quando há consumo. 4 min

Passo 1 de 8
Aplique uma função nomeada aos valores de uma fonte somente quando cada resultado for solicitado.
map(funcao, fonte) cria um iterador de resultados. Ele guarda a função e a fonte, mas não chama a função para todos os valores no momento da criação.
Use uma função nomeada quando o nome ajuda a deixar clara a responsabilidade da etapa.
def dobrar(numero):
print(f"transformando {numero}")
return numero * 2
numeros = [2, 4, 6]
dobrados = map(dobrar, numeros)
print(dobrados)
Após criar dobrados, a função dobrar ainda não foi chamada.
Ao pedir o próximo resultado com next(dobrados), o map obtém o próximo elemento da fonte, chama dobrar para esse elemento e entrega o retorno.
Assim, duas chamadas a next transformam somente os dois primeiros valores.
print(next(dobrados))
print(next(dobrados))Exemplo
transformando 2
4
transformando 4
8A primeira solicitação chama dobrar(2) e entrega 4. A segunda chama dobrar(4) e entrega 8. O valor 6 continua sem transformação até que alguém solicite outro resultado.

Cada resultado solicitado corresponde a uma chamada de dobrar neste exemplo.
Considere o código:
chamadas = []
def triplicar(valor):
chamadas.append(valor)
return valor * 3
resultados = map(triplicar, [1, 2, 3])
primeiro = next(resultados)Quais são os valores de primeiro e chamadas após esse código?

Passo 2 de 8
Selecione valores sob demanda com um predicado e acompanhe os candidatos examinados até cada resultado aceito.
Use filter(predicado, fonte) para criar um iterador que procura elementos cujo predicado retorne True. Ele não executa a busca ao ser criado: o trabalho acontece quando alguém solicita o próximo resultado.
Diferentemente de map, filter devolve o próprio valor da fonte quando ele é aceito. O predicado apenas decide se esse valor segue adiante.
Crie este fluxo e consuma seus resultados um a um.
def eh_par(numero):
return numero % 2 == 0
numeros = iter([3, 8, 11, 14])
pares = filter(eh_par, numeros)
print(next(pares)) # 8
print(next(pares)) # 14Para entregar 8, o filtro precisou avaliar 3 e depois 8. O valor entregue continua sendo 8, não o resultado booleano True.

O predicado decide o caminho; o elemento aceito é preservado.
Ao chamar next(pares) pela primeira vez, filter examina 3 (rejeita) e 8 (aceita), então entrega 8. Na chamada seguinte, retoma de onde parou: examina 11 e depois entrega 14.
Assim, saídas entregues, candidatos examinados e chamadas do predicado são contagens relacionadas, mas não necessariamente iguais.
A lista avaliados mostra exatamente quais candidatos chegaram ao predicado.
avaliados = []
def eh_par_registrando(numero):
avaliados.append(numero)
return numero % 2 == 0
pares = filter(eh_par_registrando, [3, 8, 11, 14])
print(avaliados) # []: ainda não houve consumo
print(next(pares)) # 8
print(avaliados) # [3, 8]
print(next(pares)) # 14
print(avaliados) # [3, 8, 11, 14]Dica
A criação de pares não chama o predicado. Cada candidato examinado gera uma chamada; somente os que retornam True aparecem na saída.
Considere filter(eh_par, [5, 6, 9, 10]). Para produzir a primeira saída, o filtro examina ____ candidatos.
Com filter(eh_par, [5, 6, 9, 10]), qual é o primeiro valor entregue por next(...)?

Passo 3 de 8
Una fontes em uma única sequência de consumo, preservando a ordem e sem criar uma lista concatenada.
itertools.chain recebe fontes e produz um iterador único. Ele entrega todos os elementos da primeira fonte; somente quando ela se esgota, passa para a próxima.
Isso preserva tanto a ordem dentro de cada fonte quanto a ordem em que você informou as fontes.
Observe que não há alternância entre as fontes: a segunda só começa após o fim da primeira.

chain(fonte_a, fonte_b) produz primeiro toda fonte_a e depois toda fonte_b.
Execute o código e observe quando cada mensagem é exibida.
from itertools import chain
def fonte(nome, valores):
for valor in valores:
print(f"{nome} fornece {valor}")
yield valor
primeira = fonte("primeira", ["A", "B"])
segunda = fonte("segunda", ["C", "D"])
fluxo = chain(primeira, segunda)
print("Fluxo criado")
print(next(fluxo))
print(list(fluxo))Ao criar fluxo, nenhuma fonte é avançada. O primeiro next(fluxo) pede A à primeira fonte. Depois, list(fluxo) consome o restante: B e, só então, C e D da segunda fonte.
chain não monta uma lista concatenada antecipadamente. Ele mantém o consumo incremental das fontes.
Dica
Com ["A", "B"] e ["C", "D"], chain entrega A, B, C, D — não A, C, B, D. Alternar elementos por posição é outra operação.
Considere:
from itertools import chain
numeros = [2, 4]
letras = ["x", "y"]
resultado = chain(numeros, letras)Organize os valores na ordem em que list(resultado) os entrega.

Passo 4 de 8
Nomeie cada etapa de um fluxo e use registros para observar o que acontece na montagem e no consumo.
Em vez de aninhar várias chamadas em uma única linha, dê um nome para cada etapa. Isso deixa claro o papel de cada uma: fonte fornece valores, selecionados aplica o filtro, transformados aplica a transformação e limitados expõe apenas a quantidade desejada.
A função de composição abaixo deve retornar o iterador final. Ela não deve chamar list, next nem usar um for para consumir o fluxo.
O consumidor pede uma saída; as etapas anteriores só trabalham o necessário para atendê-lo.

Os valores seguem das fontes ao consumidor; cada pedido do consumidor se propaga no sentido contrário.
Dica
Nomear etapas não torna o processamento menos incremental. Os nomes apenas guardam referências aos iteradores conectados.
Copie o código para um arquivo, por exemplo fluxo.py, e execute-o. A lista registro permite observar exatamente quando a fonte, o predicado e a transformação foram acionados.
from itertools import chain, islice
def fonte_registrada(valores, registro):
for valor in valores:
registro.append(f"fonte:{valor}")
yield valor
def eh_par(valor, registro):
registro.append(f"predicado:{valor}")
return valor % 2 == 0
def dobrar(valor, registro):
registro.append(f"transformacao:{valor}")
return valor * 2
def compor_fluxo(primeira, segunda, registro):
fonte = chain(
fonte_registrada(primeira, registro),
fonte_registrada(segunda, registro),
)
selecionados = filter(lambda valor: eh_par(valor, registro), fonte)
transformados = map(lambda valor: dobrar(valor, registro), selecionados)
limitados = islice(transformados, 2)
return limitados
registro = []
fluxo = compor_fluxo([1, 2], [3, 4], registro)
print("Após montar:", registro)
print("Primeira saída:", next(fluxo))
print("Após a primeira saída:", registro)
print("Restante:", list(fluxo))
print("Após consumir tudo:", registro)Exemplo
Após montar, o registro é []: criar e conectar os iteradores não chamou a fonte, o predicado nem a transformação.
Na primeira chamada de next(fluxo), o fluxo examina 1 e o rejeita; depois examina 2, aceita-o e o transforma. Portanto, a primeira saída é 4, e os eventos registrados são, nesta ordem: fonte:1, predicado:1, fonte:2, predicado:2, transformacao:2.
No seu computador, execute o programa até a linha Após a primeira saída. Qual é a diferença entre o registro após montar o fluxo e o registro após a primeira saída? Explique quais etapas participaram e por que o valor 1 aparece no registro mesmo não sendo entregue ao consumidor.
Escreva pelo menos 100 caracteres (0/100).

Passo 5 de 8
Compare a ordem de filter e map para preservar o critério de seleção e evitar trabalho desnecessário quando isso for válido.
Em um fluxo, cada etapa recebe a saída da etapa anterior. Portanto, em map(transformar, filter(aceitar, dados)), o predicado aceitar recebe os valores originais. Já em filter(aceitar, map(transformar, dados)), ele recebe valores já transformados.
Mover o mesmo predicado não é apenas uma mudança de posição: pode mudar o significado da seleção.

À esquerda, o filtro avalia a entrada; à direita, avalia a saída de map.
Execute o código e observe os valores materializados ao final de cada fluxo.
dados = [1, 2, 3, 4]
def multiplicar_por_10(valor):
return valor * 10
def maior_que_2(valor):
return valor > 2
# O predicado recebe os valores originais.
filtrar_antes = map(
multiplicar_por_10,
filter(maior_que_2, dados),
)
# O mesmo predicado agora recebe valores multiplicados por 10.
filtrar_depois = filter(
maior_que_2,
map(multiplicar_por_10, dados),
)
print(list(filtrar_antes)) # [30, 40]
print(list(filtrar_depois)) # [10, 20, 30, 40]Atenção
Filtrar antes só é apropriado se o critério fizer sentido para o dado de entrada. Se a regra de negócio depende do valor transformado, mantenha o filtro depois de map.
Se multiplicar por 10 e o critério desejado é “resultado maior que 20”, há duas formas equivalentes:
valor > 2 e depois multiplicar;valor > 20.O predicado foi adaptado porque cada posição trabalha em uma escala diferente.
Cada execução cria seu próprio registro. Ambas entregam os mesmos resultados, mas não executam a transformação na mesma quantidade de candidatos.
dados = [1, 2, 3, 4]
def executar(filtro_antes):
transformados = []
def multiplicar_por_10(valor):
transformados.append(valor)
return valor * 10
if filtro_antes:
fluxo = map(
multiplicar_por_10,
filter(lambda valor: valor > 2, dados),
)
else:
fluxo = filter(
lambda valor: valor > 20,
map(multiplicar_por_10, dados),
)
return list(fluxo), transformados
print(executar(True)) # ([30, 40], [3, 4])
print(executar(False)) # ([30, 40], [1, 2, 3, 4])Dica
Quando o filtro sobre a entrada preserva a regra de seleção, colocá-lo antes evita chamar a transformação para candidatos rejeitados. Isso mostra menos trabalho no registro; não é uma regra universal de ordenação.
Relacione cada fluxo à combinação correta de valores entregues e valores recebidos pela função de transformação.
Toque em um item e depois no par correspondente.

Passo 6 de 8
Compare o limite aplicado aos candidatos com o limite aplicado aos resultados aceitos e observe o consumo da fonte.
A posição de islice define o que está sendo limitado.
filter(predicado, islice(fonte, 4)), só quatro candidatos chegam ao filtro. Se houver rejeições, podem sair menos de quatro valores.islice(filter(predicado, fonte), 4), o filtro continua pedindo candidatos até entregar quatro valores aceitos ou até a fonte se esgotar.Portanto, um limite depois de filter pode fazer a fonte avançar bem além da quantidade de saídas desejada.
Compare os dois percursos do fluxo.

À esquerda, o limite fecha a entrada do filtro. À direita, o filtro busca candidatos até completar o limite de resultados aceitos.
Execute o código. Cada chamada de executar cria fontes e listas de registro novas; assim, os dados observados em um fluxo não se misturam aos do outro.
A primeira composição limita quatro candidatos. A segunda solicita dois resultados pares, por isso precisa passar pelos ímpares até encontrar dois pares.
Crie um arquivo Python, cole o código e execute-o.
from itertools import chain, islice
def fonte(nome, valores, fornecidos):
for valor in valores:
fornecidos.append((nome, valor))
yield valor
def par(valor, avaliados):
avaliados.append(valor)
return valor % 2 == 0
def transformar(valor, transformados):
transformados.append(valor)
return valor * 10
def executar(limite_antes_do_filtro):
fornecidos = []
avaliados = []
transformados = []
primeira = fonte("primeira", [1, 3, 4], fornecidos)
segunda = fonte("segunda", [5, 6, 8], fornecidos)
encadeada = chain(primeira, segunda)
if limite_antes_do_filtro:
candidatos_limitados = islice(encadeada, 4)
aceitos = filter(lambda valor: par(valor, avaliados), candidatos_limitados)
fluxo = map(lambda valor: transformar(valor, transformados), aceitos)
else:
aceitos = filter(lambda valor: par(valor, avaliados), encadeada)
transformados_sob_demanda = map(
lambda valor: transformar(valor, transformados),
aceitos,
)
fluxo = islice(transformados_sob_demanda, 2)
resultado = list(fluxo)
print("resultado:", resultado)
print("fornecidos:", fornecidos)
print("avaliados:", avaliados)
print("transformados:", transformados)
print("Limite de 4 candidatos:")
executar(limite_antes_do_filtro=True)
print("\nLimite de 2 resultados aceitos:")
executar(limite_antes_do_filtro=False)As fontes encadeadas produzem [1, 3, 4] e depois [5, 6, 8]. Você precisa obter os dois primeiros valores pares, transformados por * 10. Qual composição atende ao requisito e quantos elementos da fonte são solicitados?

Passo 7 de 8
Entenda por que sorted interrompe a entrega incremental e como sua posição em relação a islice altera tanto o consumo quanto o significado do resultado.
sorted devolve uma nova lista ordenada. Para decidir qual valor vem primeiro, ele precisa consumir toda a entrada finita antes de disponibilizar qualquer resultado.
Assim, um fluxo pode ser incremental antes de sorted, mas deixa de entregar valores sob demanda nesse ponto.
Mesmo que o consumidor peça apenas um valor depois de sorted, a etapa de ordenação precisa receber todos os valores da fonte.

sorted materializa a entrada completa em uma lista antes de liberar o primeiro elemento.
Atenção
Em islice(sorted(fonte), 3), o limite entrega no máximo três resultados, mas sorted já consumiu toda a fonte. Portanto, esse limite não reduz o trabalho exigido pela ordenação.
Compare as duas composições:
sorted(islice(fonte, 3)): pega os três primeiros valores fornecidos e os ordena.islice(sorted(fonte), 3): ordena todos os valores da fonte e então pega os três menores.As duas usam limite e ordenação, mas respondem a perguntas diferentes.
Execute este código e observe os resultados e os registros da fonte.
from itertools import islice
def fonte_registrada(valores, registro):
for valor in valores:
registro.append(valor)
yield valor
valores = [7, 2, 9, 1, 5]
registro_prefixo = []
prefixo_ordenado = sorted(
islice(fonte_registrada(valores, registro_prefixo), 3)
)
registro_global = []
menores_globais = list(
islice(sorted(fonte_registrada(valores, registro_global)), 3)
)
print(prefixo_ordenado, registro_prefixo)
print(menores_globais, registro_global)
# [2, 7, 9] [7, 2, 9]
# [1, 2, 5] [7, 2, 9, 1, 5]Dica
Converter o resultado final limitado com list(...) pode ser uma escolha adequada, por exemplo, para exibir ou reutilizar os poucos valores selecionados. O problema é criar listas intermediárias sem que alguma etapa realmente precise delas.
No fluxo islice(sorted(fonte), 2), a fonte pode permanecer parcialmente não consumida porque o limite pede apenas dois resultados.
sorted(islice(fonte, 3)) pode produzir valores diferentes de list(islice(sorted(fonte), 3)).

Passo 8 de 8
Monte um fluxo completo, execute testes locais e use os registros para comprovar que o processamento só acontece quando há consumo.
Neste desafio, duas fontes finitas são encadeadas com chain. O fluxo então seleciona leituras positivas, converte as leituras aceitas e entrega somente as três primeiras conversões.
A função montar_fluxo apenas conecta as etapas e retorna o iterador final. O consumo acontece depois, em list(fluxo). Os nomes fontes, validas e convertidas deixam clara a responsabilidade de cada etapa.
O consumidor pede resultados à direita. Essa demanda volta pelas etapas até a fonte; os valores aceitos seguem no sentido contrário.

Para obter três saídas aceitas, o filtro pode precisar solicitar mais de três candidatos às fontes.
Dica
Após chamar montar_fluxo, o registro deve continuar vazio. As chamadas à fonte, ao predicado e à conversão só devem aparecer quando o consumidor avançar o iterador.
Crie um arquivo chamado fluxo_leituras.py com o código abaixo. Em seguida, crie test_fluxo_leituras.py com os testes da próxima tela. Não substitua chain, filter, map ou islice por compreensões ou listas intermediárias.
O módulo registra cada valor fornecido, cada avaliação do filtro e cada conversão.
from itertools import chain, islice
def fonte(nome, valores, registro):
for valor in valores:
registro.append(f"fonte:{nome}:{valor}")
yield valor
def criar_predicado(registro):
def eh_positivo(leitura):
registro.append(f"filtro:{leitura}")
return leitura > 0
return eh_positivo
def criar_conversor(registro):
def para_dez_unidades(leitura):
registro.append(f"map:{leitura}")
return leitura * 10
return para_dez_unidades
def montar_fluxo(lote_a, lote_b, registro):
fontes = chain(
fonte("A", lote_a, registro),
fonte("B", lote_b, registro),
)
validas = filter(criar_predicado(registro), fontes)
convertidas = map(criar_conversor(registro), validas)
return islice(convertidas, 3)
Execute no terminal, na pasta dos arquivos: pytest -q.
from fluxo_leituras import montar_fluxo
def test_montar_nao_processa_nada_antes_do_consumo():
registro = []
fluxo = montar_fluxo([4, -1, 8], [0, 5, 12], registro)
assert registro == []
assert list(fluxo) == [40, 80, 50]
def test_consumo_para_apos_tres_resultados_aceitos():
registro = []
resultado = list(montar_fluxo([4, -1, 8], [0, 5, 12], registro))
assert resultado == [40, 80, 50]
assert registro == [
"fonte:A:4", "filtro:4", "map:4",
"fonte:A:-1", "filtro:-1",
"fonte:A:8", "filtro:8", "map:8",
"fonte:B:0", "filtro:0",
"fonte:B:5", "filtro:5", "map:5",
]
assert "fonte:B:12" not in registro
Depois de executar os testes, relate: (1) os valores entregues; (2) o que o registro mostrou antes do consumo; e (3) uma alteração de ordem — como mover islice ou inserir sorted — e seu efeito sobre o resultado ou sobre o trabalho realizado.
Escreva pelo menos 180 caracteres (0/180).
Resumo
Use estes critérios ao revisar um fluxo sob demanda.
chain, sem criar uma lista concatenada.list, inicia o trabalho.filter pode examinar vários candidatos para entregar uma saída; map só transforma os valores aceitos que são solicitados.islice muda se o limite vale para candidatos ou para resultados aceitos.sorted consome toda a entrada antes de disponibilizar resultados; um limite posterior não reduz esse consumo.Parabéns! Você concluiu: Compor fluxos de transformação sob demanda
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