A função ` reduce() ` da biblioteca Python vem do mundo da programação funcional. Programação funcional (FP) é um jeito de programar em que os programas criam resultados aplicando funções a dados que não mudam.
Um padrão comum nesse estilo é o “fold”, que transforma uma sequência em um único resultado. Por exemplo, dobrando a lista de números [2, 4, 5, 3] sob a adição 14 através de etapas sucessivas: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce() generaliza essa ideia. Ele aplica uma operação binária em um iterável até que só o resultado fique.
Neste artigo, explorarei os principais elementos do Python ( reduce() ) e darei alguns exemplos práticos. Se você precisa dar uma repassada nos conceitos básicos do Python, recomendo dar uma olhada nesses recursos:
Python inclui outras funções de programação funcional em Python, como map() e filter().
Construções funcionais como map() e filter() estavam no Python 1.0. Guido van Rossum não gostava deles, apontando que reduce() era difícil de analisar, e que um loop for é quase sempre mais legível. No Python 3.0, seguindo o PEP 3100, os desenvolvedores tiraram o ` reduce() ` como um built-in e colocaram ele no módulo ` functools `. Essa mudança para functools basicamente rebaixou o programa para o status de uma ferramenta de nicho.
Por que usar reduce()?
Na maioria das vezes, acho que provavelmente é melhor usar um loop ou um loop interno. Mas, oreduce() ainda é uma boa opção em alguns casos.
Funcionam como pipelines. Faça uma série de transformações (que podem ser dinâmicas) de um jeito bem organizado.
Dobras algébricas. Use para operações que têm valores de identidade naturais, como união de conjuntos com o conjunto vazio ou operações de máscara de bits com zero.
Dobragem personalizada sem integração. Defina sua própria fusão de um jeito específico para o domínio quando não tiver nenhuma integrada.
Acumuladores estruturados. Programa várias partes do estado ao mesmo tempo dentro de uma função acumuladora personalizada.
A função reduce() aceita dois argumentos obrigatórios e um terceiro opcional.
function: uma função binária que diz como juntar dois elementos,
iterable: a sequência ou iterável a ser reduzida, como uma lista ou tupla, initializer(opcional): um valor inicial para semear a função.
Exemplo passo a passo:
[1, 3, 2, 7] → [4, 2, 7].
[4, 2, 7] → [6, 7].
[6, 7] → [13].
O resultado final é 13.
Exemplos simples de reduce()
Os exemplos a seguir mostram como usar o reduce().
from functools importreduce
numbers =[2,4,6]
product =reduce(lambda x, y: x * y, numbers)# ((2 x 4) x 6) = 8 x 6 = 48
min_value =reduce(lambda x, y: x if x < y else y, numbers)# 2
words =['dog','cat','tree','pony']
str_concat =reduce(lambda x, y: x + y, words)# "dogcattreepony"Tecnologia
Inicializador
Sem um inicializador, reduce() pega o primeiro elemento do iterável como seu valor inicial. Se o iterável estiver vazio, reduce() lança uma exceção TypeError. Para deixar o código mais robusto, use um inicializador para definir o que fazer quando não tiver nada na entrada.
from functools importreduce
words =[]# Error: reduce() of empty iterable with no initial value
str_concat =reduce(lambda x, y: x + y, words)# Correct: use empty string initializer
str_concat =reduce(lambda x, y: x + y, words,"")Tecnologia
Os inicializadores também geram um resultado com um contêiner vazio. Por exemplo, você pode juntar palavras numa lista simples de caracteres.
Até agora, usamos funções lambda para definir o operador binário.
Você também pode usar operadores do módulo operator. O módulooperator tem versões de funções de operadores comuns e chamadas de método. Por exemplo, em vez de x + y, operator.add(x, y). Isso permite que você passe operadores pré-definidos (e eficientes) para um reduce() sem precisar escrever um lambda.
from functools importreduceimport operator as op
numbers =[2,4,6]
total =reduce(op.add, numbers,0)# instead of reduce(lambda x,y: x + y, numbers)Tecnologia
Uma terceira opção é escrever uma função personalizada. Essa é uma boa opção quando não tem um operador pré-definido ou a função é muito complicada para um lambda.
Por exemplo, digamos que você queira tirar as duplicatas de uma lista, mas manter a ordem em que elas aparecem pela primeira vez. Você pode definir um redutor que acrescente um item a uma lista apenas se ele não tiver aparecido antes.
from functools importreduce
items =['the','wild','wild','world','is','the','wide','world','is','the','world']defdedup(acc, x):if x notin acc:# O(n) membership test
acc.append(x)return acc
unique =reduce(dedup, items,[])# ['the', 'wild', 'world', 'is', 'wide']Tecnologia
Para mais dicas sobre como eliminar duplicatas, dá uma olhada neste tutorial.
Python reduce() chama nossa função uma vez para cada elemento. Em uma lista com um milhão de itens, isso significa um milhão de chamadas de função, cada uma das quais cria um quadro, lida com argumentos e atualiza contagens de referência. Isso adiciona uma sobrecarga significativa.
Por outro lado, um built-in como sum faz uma única chamada para uma função C e faz um milhão de adições dentro do loop C. Essa diferença pode tornar as ordens de magnitude integradas mais rápidas.
Problemas de localidade do cache e eficiência da CPU
O Reduce também tem problemas com a localidade do cache e a eficiência da CPU.
Uma CPU moderna consegue fazer vários bilhões de instruções por segundo, mas o acesso à RAM é bem mais lento. Pra compensar, as CPUs modernas têmcaches d e (L1, L2, L3) que guardam dados pra acesso rápido.
Esses caches usam dois padrões.
Localidade temporal Os dados usados recentemente provavelmente serão usados de novo.
Localidade espacial Os dados próximos a um endereço usado recentemente provavelmente serão usados em breve.
Por outro lado, cada etapa de uma busca por ponteiros ( reduce() ) envolve a busca por ponteiros para encontrar o próximo elemento e chamadas de funções Python, o que quebra a localidade e atrasa a CPU. Funções embutidas e vetorizadas evitam esse problema ao executar loops C restritos.
Pra dar uma repassada sobre como escrever código Python eficiente e idiomático, dá uma olhada em:
Código C otimizado. Os built-ins rodam seus loops em código C otimizado, não em Python. Isso evita a sobrecarga que o reduce() causa. Essa vantagem de velocidade aumenta com grandes entradas.
Legibilidade. Os built-ins têm nomes descritivos (sum, min), então o que eles fazem é bem claro. Uma chamada para reduce() faz com que você analise a função que está sendo dobrada.
Loops:
Desempenho. Os loops geralmente são mais lentos do que os built-ins, mas mais rápidos do que umreduce().
Legibilidade. Assim como os built-ins, os loops geralmente são mais fáceis de ler do que um reduce(). Uma chamada para reduce() obriga você a analisar uma instrução funcional, enquanto um loop é mais Python.
itertools.accumulate()
A biblioteca Python itertools oferece um monte de iteradores eficientes, como count(), product() e combinations(). Uma função útil do itertools é itertools.accumulate(). Assim como reduce(), ele aplica uma função sobre um iterável. Mas, accumulate() guarda os valores intermediários do cálculo, não só o resultado final.
Acumular é útil quando você precisa de totais acumulados ou mínimos/máximos. Por exemplo, você pode querer saber a temperatura máxima mês a mês.
Erros comuns ao usar reduce()
Quando você usar oreduce(), fique atento às seguintes armadilhas.
Prefira alternativas mais simples, como built-ins ou loops. Guarde o site reduce() para quando você realmente precisar dele.
Lidar com iteráveis vazios. Sempre use um inicializador apropriado para evitar erros em entradas vazias.
Fica de olho nos problemas de memória. Não tente forçar o uso de um gerador de chaves (reduce() ) em situações em que um gerador ou uma abordagem de streaming seriam mais eficientes.
Evite lambdas complicadas. Use as funções do módulo ` operator ` sempre que puder. Lambdas, principalmente com operações não associativas, podem prejudicar a clareza.
Prefira a clareza à esperteza.
Melhores práticas e diretrizes para reduce() em Python
Como com qualquer ferramenta, tem as melhores práticas com o reduce(). Se você decidiu que reduce() é a ferramenta certa pra usar, aqui vão algumas dicas de como usá-la.
Primeiro, projete o redutor.
Defina o contrato em uma frase. “Junte as chaves do dicionário somando as contagens por chave.”
Mantenha-o associativo, se possível. Isso permite que você paralelize e teste com mais facilidade.
Identifique o seu elemento de identidade e use-o como inicializador. Por exemplo, para a soma, a identidade é 0. Para min, é math.inf, e para set union, é set(). Isso mantém o código robusto e livre de TypeErrors.
Mantenha a simplicidade e a legibilidade
Uma operação simples. Sem efeitos colaterais.
Dê um nome que descreva bem o redutor.
Documento
Na string de documentação, anote o elemento de identidade e o comportamento de entrada vazia, a suposição de associatividade e a política de erros.
Compare entradas pequenas e grandes. Compare benchmarks com funções internas e loops.
Se a velocidade é importante, pense em pré-processar, fazer em lotes e passar cálculos pesados para o NumPy ou pandas.
Aplicações avançadas e casos de uso reais para Python reduce()
Considerando as desvantagens, pode parecer que o reduce() não tem nenhum valor real. Pelo contrário, reduce() tem muitos casos de uso úteis.
Processando estruturas aninhadas
Operações do tipo banco de dados
Pipelines de processamento de dados
Aplicativos MapReduce
Processando estruturas aninhadas
O Reduce oferece uma maneira simples de percorrer estruturas de dados aninhadas, como objetos JSON, dobrando uma sequência de chaves em pesquisas sucessivas.
import json
from functools importreduceimport operator
data = json.loads('''
{
"user": {
"id": "ABC123",
"name": "Alice",
"email": "[email protected]",
"profile": {
"address": {
"city": "San Francisco",
"zip": "94103"
},
"age": 34,
"skills": ["Python", "Data Science", "Machine Learning"]
}
}
}
''')# Example lookups with reduce + operator.getitem
city =reduce(operator.getitem,["user","profile","address","city"], data)print(city)# "San Francisco"
user_id =reduce(operator.getitem,["user","id"], data)print(user_id)# "ABC123"
age =reduce(operator.getitem,["user","profile","age"], data)print(age)# 34Tecnologia
Usar reduce() faz sentido aqui. O JSON está bem aninhado: user → profile → address → city. Em vez de encadear pesquisas manualmente, represente o caminho como uma lista de chaves. Depois, usa reduce(operator.getitem, path, data) para percorrê-lo. Isso mantém o código genérico, legível e reutilizável.
Pipelines de processamento de dados
O Reduce pode conduzir pipelines de processamento de dados, passando os dados por uma sequência de transformações. Cada função cuida de uma etapa, e o pipeline é o resultado de aplicá-las na ordem certa. Aqui está um pipeline de brinquedo que pré-processa uma sequência de texto antes de alimentá-la em um modelo de NLP.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return s.lower()defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(word for word in s.split()if word notin stops)defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(word[:-3]if word.endswith("ing")else word for word in s.split())
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logTecnologia
Tratamento de erros em aplicativos complexos
Vamos voltar ao exemplo JSON aninhado. Agora, a chamada direta para reduce(operator.getitem, …) gera uma exceção KeyError ou TypeError se uma chave estiver faltando ou se encontrar um objeto que não seja um dicionário. Para deixar o código mais seguro, crie uma função auxiliar que envolva operator.getitem em um bloco try/except e retorne um valor padrão quando ocorrer um erro.
Aqui está uma possibilidade para a função auxiliar.
Agora, mude as pesquisas de exemplo para usar nossa nova função em vez de uma função desempacotada. reduce()
# Example lookups
city = deep_get(data,["user","profile","address","city"], default="Unknown City")print(city)# "San Francisco"
user_id = deep_get(data,["user","id"], default="N/A")print(user_id)# "ABC123"
age = deep_get(data,["user","profile","age"], default="N/A")print(age)# 34# Example with missing key
phone = deep_get(data,["user","profile","phone"], default="No phone")print(phone)# "No phone"Tecnologia
Transformações de dados em várias etapas com map() e filter()
Você pode juntar o reduce() com outras ferramentas úteis, como map() e filter(), para criar transformações de dados em várias etapas. Aqui está o nosso pipeline de pré-processamento de PNL anterior, escrito de forma funcional.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return" ".join(map(str.lower, s.split()))defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(filter(lambda w: w notin stops, s.split()))defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(map(lambda w: w[:-3]if w.endswith("ing")else w, s.split()))# Pipeline of transformations
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logTecnologia
As transformações são:
Tira a pontuação. “A rápida raposa marrom está pulando sobre um tronco.” → “A rápida raposa marrom está pulando sobre um tronco.”
Letras minúsculas
“A rápida raposa marrom está pulando sobre um tronco” → “a rápida raposa marrom está pulando sobre um tronco”
Tira as palavras irrelevantes. “A rápida raposa marrom está pulando sobre um tronco” → “rápida raposa marrom pulando sobre tronco”
Pra usar bem o reduce(), é legal entender como ele se encaixa no ecossistema Python moderno. Vamos ver como ele se encaixa ao lado do NumPy e do pandas, como ele sustenta sistemas paralelos e distribuídos e como ele interage com ferramentas modernas, como analisadores estáticos.
numpy e pandas
O NumPy e o pandas funcionam a partir de código C otimizado, então não duplique suas funcionalidades com reduce(). Mas, o reduce() é uma boa opção para pipelines com etapas dinâmicas. Por exemplo, você pode compor várias transformações NumPy em uma matriz.
from functools importreduceimport numpy as np
defstandardize(x):return(x - x.mean())/(x.std()+1e-9)defclip(x):return np.clip(x,0,1)deflog(x):return np.log1p(x)
x = np.array([1,500,40.5,100,250.45])
funcs =[standardize, clip, log]
y =reduce(lambda a, f: f(a), funcs, x)# x is ndarrayTecnologia
Estruturas de computação paralela e distribuída
A redução é essencial para estruturas de computação paralela e distribuída. Esses sistemas funcionam dividindo um conjunto de dados em partições, processando essas partições em paralelo e, em seguida, combinando esses resultados parciais em uma única resposta. A etapa de “combinar” é a redução.
Para que a redução funcione corretamente em um cluster, certifique-se de que as seguintes condições sejam atendidas.
Associatividade. A operação deve dar o mesmo resultado, não importa como você agrupar. Isso permite que resultados parciais sejam combinados em qualquer ordem na rede.
Elemento de identidade. A operação precisa ter um inicializador que não mexa no resultado final. Isso garante a correção quando as partições estão vazias ou têm tamanhos diferentes.
Se essas propriedades não forem mantidas, as reduções ficam lentas (porque não podem ser paralelizadas com segurança) ou incorretas (porque os resultados dependem da ordem de avaliação).
Esse processo precisa de uma operação associativa e uma identidade clara. Caso contrário, você vai acabar com um código lento ou resultados errados.
Ferramentas de análise estática
Um analisador estático (como mypy, Pyright, ruff ou bandit) é uma ferramenta que dá uma olhada no código sem precisar rodá-lo. Essas ferramentas detectam bugs, aplicam regras de estilo e verificam a correção dos tipos.
Os analisadores estáticos têm dificuldade com reduce(). No processo de dobragem, o tipo de acumulador pode ser diferente do tipo de elemento, e a inferência de tipo fica confusa.
Dá uma olhada nesse código.
from functools importreducedefadd_chars(acc, word):
acc.extend(word)# extend adds each character of the stringreturn acc
chars =reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Tecnologia
Mesmo que esse código funcione bem, um analisador estático pode reclamar de algumas coisas.
O tipo de elemento do inicializador [] é meio confuso.
Os analisadores podem presumir que os tipos acumulador e elemento são compatíveis.
Para deixar o código mais claro para humanos e analisadores, adicione dicas de tipo.
from functools importreducefrom typing import List
defadd_chars(acc: List[str], word:str)-> List[str]:
acc.extend(word)# extend adds each character of the stringreturn acc
chars: List[str]=reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Tecnologia
Agora, o redutor mostra explicitamente
O acumulador é um List[str].
Cada elemento é um str.
O tipo de retorno é um objeto de retorno de tipo(list[str]).
Com essas dicas, os analisadores estáticos podem verificar o código com rigor.
Conclusão
Reduce vem da programação funcional, onde juntar coleções em um único resultado é uma ideia central. Mesmo que não seja mais uma ferramenta de primeira classe no Python, ela ainda tem seu lugar. Quando você precisa de pipelines flexíveis, dobras personalizadas ou operações que não se encaixam perfeitamente nas funções existentes, o reduce() é uma ferramenta poderosa. Usado com cuidado, ele se integra perfeitamente ao ecossistema Python e continua sendo uma ferramenta prática nas situações certas.
Alguns links relacionados ao Python que podem ser úteis.
Descubra como escrever funções em Python reutilizáveis e eficientes. Domine parâmetros, instruções de retorno e temas avançados como funções lambda. Organize melhor seu código com main() e outras boas práticas.
Saiba tudo sobre a compreensão de dicionário do Python: como você pode usá-la para criar dicionários, substituir loops for (aninhados) ou funções lambda por map(), filter() e reduce(), ...!
Aprenda sobre as funções lambda do Python, para que servem e quando usá-las. Inclui exemplos práticos e melhores práticas para uma implementação eficaz.
A função `
reduce()` da biblioteca Python vem do mundo da programação funcional. Programação funcional (FP) é um jeito de programar em que os programas criam resultados aplicando funções a dados que não mudam.Um padrão comum nesse estilo é o “fold”, que transforma uma sequência em um único resultado. Por exemplo, dobrando a lista de números [2, 4, 5, 3] sob a adição 14 através de etapas sucessivas: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce()generaliza essa ideia. Ele aplica uma operação binária em um iterável até que só o resultado fique.Neste artigo, explorarei os principais elementos do Python (
reduce()) e darei alguns exemplos práticos. Se você precisa dar uma repassada nos conceitos básicos do Python, recomendo dar uma olhada nesses recursos:História
Python inclui outras funções de programação funcional em Python, como
map()efilter().Construções funcionais como
map()efilter()estavam no Python 1.0. Guido van Rossum não gostava deles, apontando quereduce()era difícil de analisar, e que um loopforé quase sempre mais legível. No Python 3.0, seguindo o PEP 3100, os desenvolvedores tiraram o `reduce()` como um built-in e colocaram ele no módulo `functools`. Essa mudança parafunctoolsbasicamente rebaixou o programa para o status de uma ferramenta de nicho.Por que usar reduce()?
Na maioria das vezes, acho que provavelmente é melhor usar um loop ou um loop interno. Mas, o
reduce()ainda é uma boa opção em alguns casos.Como funciona a função reduce() do Python
Vamos ver como funciona o site
reduce().A assinatura da função básica de
reduce()éA função
reduce()aceita dois argumentos obrigatórios e um terceiro opcional.function: uma função binária que diz como juntar dois elementos,iterable: a sequência ou iterável a ser reduzida, como uma lista ou tupla,initializer(opcional): um valor inicial para semear a função.Exemplo passo a passo:
O resultado final é 13.
Exemplos simples de reduce()
Os exemplos a seguir mostram como usar o
reduce().Inicializador
Sem um inicializador,
reduce()pega o primeiro elemento do iterável como seu valor inicial. Se o iterável estiver vazio,reduce()lança uma exceçãoTypeError. Para deixar o código mais robusto, use um inicializador para definir o que fazer quando não tiver nada na entrada.Os inicializadores também geram um resultado com um contêiner vazio. Por exemplo, você pode juntar palavras numa lista simples de caracteres.
Pra explorar mais o Python e a manipulação de dados, aqui estão algumas opções que eu recomendo.
Definindo o redutor
Até agora, usamos funções lambda para definir o operador binário.
Você também pode usar operadores do módulo
operator. O módulooperatortem versões de funções de operadores comuns e chamadas de método. Por exemplo, em vez de x + y,operator.add(x, y). Isso permite que você passe operadores pré-definidos (e eficientes) para umreduce() sem precisar escrever um lambda.Uma terceira opção é escrever uma função personalizada. Essa é uma boa opção quando não tem um operador pré-definido ou a função é muito complicada para um lambda.
Por exemplo, digamos que você queira tirar as duplicatas de uma lista, mas manter a ordem em que elas aparecem pela primeira vez. Você pode definir um redutor que acrescente um item a uma lista apenas se ele não tiver aparecido antes.
Para mais dicas sobre como eliminar duplicatas, dá uma olhada neste tutorial.
Desempenho do reduce() em Python
reduce()tem a ver com problemas de desempenho.Sobrecarga da chamada de função
Python
reduce()chama nossa função uma vez para cada elemento. Em uma lista com um milhão de itens, isso significa um milhão de chamadas de função, cada uma das quais cria um quadro, lida com argumentos e atualiza contagens de referência. Isso adiciona uma sobrecarga significativa.Por outro lado, um built-in como
sumfaz uma única chamada para uma função C e faz um milhão de adições dentro do loop C. Essa diferença pode tornar as ordens de magnitude integradas mais rápidas.Problemas de localidade do cache e eficiência da CPU
O Reduce também tem problemas com a localidade do cache e a eficiência da CPU.
Uma CPU moderna consegue fazer vários bilhões de instruções por segundo, mas o acesso à RAM é bem mais lento. Pra compensar, as CPUs modernas têmcaches d e (L1, L2, L3) que guardam dados pra acesso rápido.
Esses caches usam dois padrões.
Por outro lado, cada etapa de uma busca por ponteiros (
reduce()) envolve a busca por ponteiros para encontrar o próximo elemento e chamadas de funções Python, o que quebra a localidade e atrasa a CPU. Funções embutidas e vetorizadas evitam esse problema ao executar loops C restritos.Pra dar uma repassada sobre como escrever código Python eficiente e idiomático, dá uma olhada em:
Alternativas ao Python reduce()
Embutidos:
reduce()causa. Essa vantagem de velocidade aumenta com grandes entradas.sum,min), então o que eles fazem é bem claro. Uma chamada parareduce()faz com que você analise a função que está sendo dobrada.Loops:
reduce().reduce(). Uma chamada parareduce()obriga você a analisar uma instrução funcional, enquanto um loop é mais Python.itertools.accumulate()A biblioteca Python
itertoolsoferece um monte de iteradores eficientes, comocount(),product()ecombinations(). Uma função útil doitertoolséitertools.accumulate(). Assim comoreduce(), ele aplica uma função sobre um iterável. Mas,accumulate() guarda os valores intermediários do cálculo, não só o resultado final.Por exemplo,
Acumular é útil quando você precisa de totais acumulados ou mínimos/máximos. Por exemplo, você pode querer saber a temperatura máxima mês a mês.
Erros comuns ao usar reduce()
Quando você usar o
reduce(), fique atento às seguintes armadilhas.reduce()para quando você realmente precisar dele.reduce()) em situações em que um gerador ou uma abordagem de streaming seriam mais eficientes.operator` sempre que puder. Lambdas, principalmente com operações não associativas, podem prejudicar a clareza.Melhores práticas e diretrizes para reduce() em Python
Como com qualquer ferramenta, tem as melhores práticas com o
reduce(). Se você decidiu quereduce()é a ferramenta certa pra usar, aqui vão algumas dicas de como usá-la.Primeiro, projete o redutor.
min, émath.inf, e paraset union, éset(). Isso mantém o código robusto e livre deTypeErrors.Mantenha a simplicidade e a legibilidade
Documento
Teste
Monitorar o desempenho
Aplicações avançadas e casos de uso reais para Python reduce()
Considerando as desvantagens, pode parecer que o
reduce()não tem nenhum valor real. Pelo contrário,reduce()tem muitos casos de uso úteis.Processando estruturas aninhadas
O Reduce oferece uma maneira simples de percorrer estruturas de dados aninhadas, como objetos JSON, dobrando uma sequência de chaves em pesquisas sucessivas.
Usar
reduce()faz sentido aqui. O JSON está bem aninhado:user → profile → address → city. Em vez de encadear pesquisas manualmente, represente o caminho como uma lista de chaves. Depois, usareduce(operator.getitem, path, data)para percorrê-lo. Isso mantém o código genérico, legível e reutilizável.Pipelines de processamento de dados
O Reduce pode conduzir pipelines de processamento de dados, passando os dados por uma sequência de transformações. Cada função cuida de uma etapa, e o pipeline é o resultado de aplicá-las na ordem certa. Aqui está um pipeline de brinquedo que pré-processa uma sequência de texto antes de alimentá-la em um modelo de NLP.
Tratamento de erros em aplicativos complexos
Vamos voltar ao exemplo JSON aninhado. Agora, a chamada direta para
reduce(operator.getitem, …)gera uma exceçãoKeyErrorouTypeErrorse uma chave estiver faltando ou se encontrar um objeto que não seja um dicionário. Para deixar o código mais seguro, crie uma função auxiliar que envolvaoperator.getitemem um bloco try/except e retorne um valor padrão quando ocorrer um erro.Aqui está uma possibilidade para a função auxiliar.
Agora, mude as pesquisas de exemplo para usar nossa nova função em vez de uma função desempacotada.
reduce()Transformações de dados em várias etapas com map() e filter()
Você pode juntar o
reduce()com outras ferramentas úteis, comomap()efilter(), para criar transformações de dados em várias etapas. Aqui está o nosso pipeline de pré-processamento de PNL anterior, escrito de forma funcional.As transformações são:
“A rápida raposa marrom está pulando sobre um tronco” → “a rápida raposa marrom está pulando sobre um tronco”
“raposa marrom rápida pulando tronco” → “raposa marrom rápida pula tronco”
Para saber mais sobre programação funcional e vetorização, a gente recomenda esses artigos do DataCamp.
Integração com o ecossistema Python moderno
Pra usar bem o
reduce(), é legal entender como ele se encaixa no ecossistema Python moderno. Vamos ver como ele se encaixa ao lado do NumPy e do pandas, como ele sustenta sistemas paralelos e distribuídos e como ele interage com ferramentas modernas, como analisadores estáticos.numpy e pandas
O NumPy e o pandas funcionam a partir de código C otimizado, então não duplique suas funcionalidades com
reduce(). Mas, oreduce()é uma boa opção para pipelines com etapas dinâmicas. Por exemplo, você pode compor várias transformações NumPy em uma matriz.Estruturas de computação paralela e distribuída
A redução é essencial para estruturas de computação paralela e distribuída. Esses sistemas funcionam dividindo um conjunto de dados em partições, processando essas partições em paralelo e, em seguida, combinando esses resultados parciais em uma única resposta. A etapa de “combinar” é a redução.
Para que a redução funcione corretamente em um cluster, certifique-se de que as seguintes condições sejam atendidas.
Se essas propriedades não forem mantidas, as reduções ficam lentas (porque não podem ser paralelizadas com segurança) ou incorretas (porque os resultados dependem da ordem de avaliação).
Esse processo precisa de uma operação associativa e uma identidade clara. Caso contrário, você vai acabar com um código lento ou resultados errados.
Ferramentas de análise estática
Um analisador estático (como mypy, Pyright, ruff ou bandit) é uma ferramenta que dá uma olhada no código sem precisar rodá-lo. Essas ferramentas detectam bugs, aplicam regras de estilo e verificam a correção dos tipos.
Os analisadores estáticos têm dificuldade com
reduce(). No processo de dobragem, o tipo de acumulador pode ser diferente do tipo de elemento, e a inferência de tipo fica confusa.Dá uma olhada nesse código.
Mesmo que esse código funcione bem, um analisador estático pode reclamar de algumas coisas.
Para deixar o código mais claro para humanos e analisadores, adicione dicas de tipo.
Agora, o redutor mostra explicitamente
List[str].str.list[str]).Com essas dicas, os analisadores estáticos podem verificar o código com rigor.
Conclusão
Reduce vem da programação funcional, onde juntar coleções em um único resultado é uma ideia central. Mesmo que não seja mais uma ferramenta de primeira classe no Python, ela ainda tem seu lugar. Quando você precisa de pipelines flexíveis, dobras personalizadas ou operações que não se encaixam perfeitamente nas funções existentes, o
reduce()é uma ferramenta poderosa. Usado com cuidado, ele se integra perfeitamente ao ecossistema Python e continua sendo uma ferramenta prática nas situações certas.Alguns links relacionados ao Python que podem ser úteis.