La función « reduce() » de Python proviene del mundo de la programación funcional. La programación funcional (FP) es un paradigma de programación en el que los programas generan resultados aplicando funciones a datos inmutables.
Un patrón común en este estilo es el «pliegue», que reduce una secuencia a un único resultado. Por ejemplo, doblar la lista de números [2, 4, 5, 3] bajo la suma 14 a través de pasos sucesivos: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce() generaliza esta idea. Aplica una operación binaria a un iterable hasta que solo queda el resultado.
En este artículo, exploraré los elementos clave de la programación orientada a objetos ( reduce(), OOP) en Python y daré algunos ejemplos prácticos. Si necesitas refrescar tus conocimientos básicos sobre Python, te recomiendo que consultes estos recursos:
Python incluye otras funciones de programación funcional en Python, como map() y filter().
Las construcciones funcionales como map() y filter() estaban en Python 1.0. A Guido van Rossum no les gustaba, señalando que reduce() era difícil de analizar, y que un bucle for casi siempre es más legible. En Python 3.0, siguiendo la PEP 3100, los programadores eliminaron reduce() como función integrada y lo trasladaron al módulo functools. Este cambio a functools básicamente lo degradó a la categoría de herramienta especializada.
¿Por qué utilizar reduce()?
La mayoría de las veces, creo que probablemente sea mejor opción utilizar un bucle o una función integrada. Sin embargo, reduce() sigue siendo una buena opción en algunos casos de uso.
Funcionan como tuberías. Encadenar una serie (posiblemente dinámica) de transformaciones de forma limpia.
Pliegues algebraicos. Úsalo para operaciones que tienen valores de identidad naturales, como la unión de conjuntos con el conjunto vacío o las operaciones de máscara de bits con cero.
Plegado personalizado sin incorporado. Define tu propia fusión de forma específica para cada dominio cuando no exista ninguna integrada.
Acumuladores estructurados. Programa simultáneamente varios elementos de estado dentro de una función acumuladora personalizada.
La función reduce() función toma dos argumentos obligatorios y un tercero opcional.
function: una función binaria que especifica cómo combinar dos elementos,
iterable: la secuencia o iterable que se va a reducir, como una lista o una tupla. initializer(opcional): un valor inicial para sembrar la función.
Ejemplo paso a paso:
[1, 3, 2, 7] → [4, 2, 7].
[4, 2, 7] → [6, 7].
[6, 7] → [13].
El resultado final es 13.
Ejemplos sencillos de reduce()
Los siguientes ejemplos ilustrativos muestran cómo utilizar 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"Con la tecnología de
Inicializador
Sin un inicializador, reduce() toma el primer elemento del iterable como su valor inicial. Si el iterable está vacío, reduce() lanza un TypeError. Para que el código sea robusto, proporciona un inicializador para definir el comportamiento ante una entrada vacía.
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,"")Con la tecnología de
Los inicializadores también generan un resultado con un contenedor vacío. Por ejemplo, puedes concatenar palabras en una lista plana de caracteres.
from functools importreduce
words =['reduce','is','fun']
chars_list =reduce(lambda acc, word: acc +list(word), words,[])print(chars_list)# ['r', 'e', 'd', 'u', 'c', 'e', 'i', 's', 'f', 'u', 'n']Con la tecnología de
Para explorar más a fondo Python y la manipulación de datos, aquí tienes algunas opciones que te recomiendo.
Hasta ahora, hemos utilizado funciones lambda para definir el operador binario.
También puedes utilizar operadores del módulo operator. El módulooperator contiene versiones funcionales de operadores comunes y llamadas a métodos. Por ejemplo, en lugar de x + y, operator.add(x, y). Esto te permite pasar operadores predefinidos (y eficientes) a reduce() sin necesidad de escribir una 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)Con la tecnología de
Una tercera opción es escribir una función personalizada. Esta es una buena opción cuando no hay un operador predefinido o la función es demasiado complicada para una lambda.
Por ejemplo, supongamos que deseas eliminar los duplicados de una lista, pero manteniendo el orden de aparición. Podrías definir un reductor que añada un elemento a una lista solo si no ha aparecido anteriormente.
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']Con la tecnología de
Para obtener más ideas sobre cómo eliminar duplicados, consulta este tutorial.
Python reduce() llama a nuestra función una vez por cada elemento. En una lista con un millón de elementos, esto significa un millón de llamadas a funciones, cada una de las cuales crea un marco, maneja argumentos y actualiza recuentos de referencias. Esto añade una sobrecarga significativa.
Por el contrario, una función integrada como sum realiza una única llamada a una función C y ejecuta el millón de sumas dentro del bucle C. Esta diferencia puede hacer que las órdenes integradas sean mucho más rápidas.
Problemas de localidad de caché y eficiencia de la CPU
Reduce también se ve afectado por la localidad de la caché y la eficiencia de la CPU.
Una CPU moderna puede ejecutar varios miles de millones de instrucciones por segundo, pero el acceso a la RAM es mucho más lento. Para compensar, las CPU modernas tienencachés de nivel (L1, L2, L3) que almacenan datos para un acceso rápido.
Estas cachés aprovechan dos patrones.
La localidad temporal Los datos utilizados recientemente probablemente se volverán a utilizar.
La localidad espacial cerca de una dirección utilizada recientemente es probable que se utilicen pronto.
Por el contrario, cada paso de reduce() implica la búsqueda de punteros para encontrar el siguiente elemento y llamadas a funciones de Python, lo que rompe la localidad y ralentiza la CPU. Las funciones integradas y vectorizadas evitan este problema ejecutándose en bucles C ajustados.
Para repasar cómo escribir código Python idiomático y eficiente, consulta:
Código C optimizado. Los componentes integrados ejecutan sus bucles en código C optimizado, no en Python. Esto evita la sobrecarga que supone reduce(). Esta ventaja en cuanto a velocidad se acentúa en entradas grandes.
Legibilidad. Los elementos integrados tienen nombres descriptivos (sum, min), por lo que su finalidad es obvia. Una llamada a reduce() hace que analices la función que se está plegando.
Bucles:
Rendimiento. Los bucles suelen ejecutarse más lentamente que las funciones integradas, pero más rápido que reduce().
Legibilidad. Al igual que las funciones integradas, los bucles suelen ser más legibles que reduce(). Una llamada a reduce() te obliga a analizar una instrucción funcional, mientras que un bucle es más Python.
itertools.accumulate()
La biblioteca ` itertools ` de Python proporciona una colección de iteradores de alto rendimiento, como ` count()`, ` product()` y ` combinations()`. Una función útil de itertools es itertools.accumulate() . Al igual que reduce(), aplica una función sobre un iterable. Sin embargo, accumulate() almacena valores intermedios del cálculo, no solo el resultado final.
Por ejemplo,
import itertools, operator
from functools importreducelist(itertools.accumulate([1,2,3,4], initial=0))# [0, 1, 3, 6, 10]reduce(operator.add,[1,2,3,4],0)# 10Con la tecnología de
La función Acumular resulta útil cuando necesitas totales acumulados o mínimos/máximos. Por ejemplo, es posible que quieras saber la temperatura máxima mes a mes.
Errores comunes al utilizar reduce()
Cuando utilices reduce(), ten en cuenta los siguientes inconvenientes.
Preferir alternativas más sencillas, como los elementos integrados o los bucles. Guarda reduce() para cuando realmente lo necesites.
Manejar iterables vacíos. Utiliza siempre un inicializador adecuado para evitar errores en entradas vacías.
Problemas de memoria del reloj. No utilices reduce() en situaciones en las que sería más eficiente utilizar un generador o un enfoque de streaming.
Evita las lambdas complicadas. Utiliza las funciones del módulo operator siempre que puedas. Las lambdas, especialmente con operaciones no asociativas, pueden perjudicar la claridad.
Prefiere la claridad a la inteligencia.
Mejores prácticas y directrices para reduce() en Python
Al igual que con cualquier herramienta, existen prácticas recomendadas con reduce(). Si has decidido que reduce() es la herramienta adecuada para ti, aquí tienes algunas pautas para su uso.
Diseña primero el reductor.
Define el contrato en una sola frase. «Combinar las claves del diccionario sumando los recuentos por clave».
Mantén la asociación si es posible. Esto te permite paralelizar y probar más fácilmente.
Identifica tu elemento de identidad y utilízalo como inicializador. Por ejemplo, para la suma, la identidad es 0. Para min, es math.inf, y para set union, es set(). Esto mantiene el código robusto y libre de TypeErrors.
Hazlo sencillo y legible.
Una operación clara. Sin efectos secundarios.
Asigna un nombre descriptivo al reductor.
Documento
En la cadena de documentación, registra el elemento de identidad y el comportamiento de entrada vacía, la suposición de asociatividad y la política de errores.
Comparar entradas pequeñas y grandes. Compara los puntos de referencia con los elementos integrados y los bucles.
Si la velocidad es importante, considera el preprocesamiento, el procesamiento por lotes y el traslado de cálculos matemáticos pesados a NumPy o pandas.
Aplicaciones avanzadas y casos de uso reales para Python reduce()
Dadas las desventajas, podría parecer que reduce() no tiene ningún valor real. Por el contrario, reduce() tiene muchos casos de uso útiles.
Procesamiento de estructuras anidadas
Operaciones de tipo base de datos
Canales de procesamiento de datos
Aplicaciones MapReduce
Procesamiento de estructuras anidadas
Reduce proporciona una forma limpia de recorrer estructuras de datos anidadas, como objetos JSON, plegando una secuencia de claves en búsquedas sucesivas.
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)# 34Con la tecnología de
En este caso, tiene sentido utilizar reduce(). El JSON está profundamente anidado: user → profile → address → city. En lugar de encadenar búsquedas manualmente, representa la ruta como una lista de claves. A continuación, utiliza reduce(operator.getitem, path, data) para recorrerlo. Esto mantiene el código genérico, legible y reutilizable.
Canales de procesamiento de datos
Reduce puede impulsar los procesos de procesamiento de datos pasando los datos por una secuencia de transformaciones. Cada función se encarga de un solo paso, y el proceso es el resultado de aplicarlas en orden. Aquí tienes un proceso de juguete que preprocesa una cadena de texto antes de introducirla en un modelo de PLN.
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 logCon la tecnología de
Gestión de errores en aplicaciones complejas
Volvamos al ejemplo de JSON anidado. En este momento, la llamada directa a reduce(operator.getitem, …) lanza una excepción KeyError o TypeError si falta una clave o si encuentra un elemento que no es un diccionario. Para que el código sea más seguro, define una función auxiliar que envuelva operator.getitem en un bloque try/except y devuelva un valor predeterminado cuando se produzca un error.
Aquí tienes una posibilidad para la función auxiliar.
defdeep_get(data, keys, default=None):"""Traverse nested dicts/lists safely with reduce."""try:returnreduce(operator.getitem, keys, data)except(KeyError, IndexError, TypeError):return defaultCon la tecnología de
Ahora, cambia las búsquedas de ejemplo para usar nuestra nueva función en lugar de una sin envolver. 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"Con la tecnología de
Transformaciones de datos en varios pasos con map() y filter()
Puedes combinar reduce() con otras herramientas funcionales, como map() y filter(), para crear transformaciones de datos de varios pasos. Aquí está vuestra canalización de preprocesamiento de PNL anterior escrita 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 logCon la tecnología de
Las transformaciones son:
Elimina la puntuación. «El rápido zorro marrón está saltando sobre un tronco». → «El rápido zorro marrón está saltando sobre un tronco».
Minúscula
«El rápido zorro marrón está saltando sobre un tronco» → «el rápido zorro marrón está saltando sobre un tronco»
Eliminar palabras vacías. «El rápido zorro marrón está saltando sobre un tronco» → «rápido zorro marrón saltando sobre tronco».
Palabras raíz
«quick brown fox jumping log» → «quick brown fox jump log»
Para profundizar en las ideas de programación funcional y vectorización, recomendamos estos artículos de DataCamp.
Para utilizar bien reduce(), es útil comprender cómo encaja en el ecosistema Python moderno. Profundicemos en cómo encaja junto con NumPy y pandas, cómo sustenta los sistemas paralelos y distribuidos, y cómo interactúa con herramientas modernas como los analizadores estáticos.
numpy y pandas
NumPy y pandas funcionan a partir de código C optimizado, así que no dupliques su funcionalidad con reduce(). Sin embargo, reduce() es una buena opción para los pipelines con pasos dinámicos. Por ejemplo, puedes componer muchas transformaciones NumPy en un arreglo.
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 ndarrayCon la tecnología de
Marcos de computación paralela y distribuida
La reducción es fundamental en los marcos de computación paralela y distribuida. Estos sistemas funcionan dividiendo un conjunto de datos en particiones, procesando esas particiones en paralelo y combinando luego los resultados parciales en una única respuesta. El paso de «combinar» es la reducción.
Para que la reducción funcione correctamente en un clúster, asegúrate de que se cumplan las siguientes condiciones.
Asociatividad. La operación debe dar el mismo resultado independientemente de la agrupación. Esto permite fusionar resultados parciales en cualquier orden a través de la red.
Elemento de identidad. La operación debe tener un inicializador que no afecte al resultado final. Esto garantiza la corrección cuando las particiones están vacías o tienen tamaños desiguales.
Si estas propiedades no se cumplen, las reducciones se vuelven lentas (porque no se pueden paralelizar de forma segura) o incorrectas (porque los resultados dependen del orden de evaluación).
Este proceso requiere una operación asociativa y una identidad clara. De lo contrario, obtendrás un código lento o resultados incorrectos.
Herramientas de análisis estático
Un analizador estático (como mypy, Pyright, ruff o bandit) es una herramienta que inspecciona el código sin ejecutarlo. Estas herramientas detectan errores, aplican reglas de estilo y comprueban la corrección tipográfica.
Los analizadores estáticos tienen dificultades con reduce(). En el proceso de plegado, el tipo de acumulador puede diferir del tipo de elemento, y la inferencia de tipos se vuelve confusa.
Considera este 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']Con la tecnología de
Aunque este código funciona correctamente, un analizador estático podría señalar algunos problemas.
El tipo de elemento del inicializador [] es ambiguo.
Los analizadores pueden suponer que los tipos del acumulador y del elemento coinciden.
Para que el código resulte más claro tanto para los humanos como para los analizadores, añade indicaciones 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']Con la tecnología de
Ahora el reductor muestra explícitamente
El acumulador es un List[str].
Cada elemento es un str.
El tipo de retorno es un objeto de tipo «list[str] ».
Con estas pistas, los analizadores estáticos pueden comprobar rigurosamente el código.
Conclusión
Reduce proviene de la programación funcional, donde la idea central es combinar colecciones en un único resultado. Aunque ya no es una herramienta de primera clase en Python, tiene su lugar. Cuando necesitas tuberías flexibles, pliegues personalizados u operaciones que no se ajustan perfectamente a las funciones existentes, reduce() es una herramienta muy potente. Si se utiliza con cuidado, se integra perfectamente en el ecosistema Python más amplio y sigue siendo una herramienta práctica en las situaciones adecuadas.
Algunos enlaces relacionados con Python que pueden resultarte útiles.
Aprende sobre las funciones lambda de Python, su finalidad y cuándo utilizarlas. Incluye ejemplos prácticos y mejores prácticas para una implementación eficaz.
Descubre cómo escribir funciones en Python reutilizables y eficientes. Domina los parámetros, las sentencias return y temas avanzados como las funciones lambda. Organiza mejor tu código con main() y otras buenas prácticas.
¡Aprende todo sobre la comprensión de diccionarios en Python: cómo puedes utilizarla para crear diccionarios, para sustituir los for loops (anidados) o las funciones lambda por map(), filter() y reduce(), ...!
Desarrolla tus habilidades con los datos, descubre cómo manipularlos y visualizarlos, y aplica análisis avanzados para tomar decisiones basadas en datos.
La función «
reduce()» de Python proviene del mundo de la programación funcional. La programación funcional (FP) es un paradigma de programación en el que los programas generan resultados aplicando funciones a datos inmutables.Un patrón común en este estilo es el «pliegue», que reduce una secuencia a un único resultado. Por ejemplo, doblar la lista de números [2, 4, 5, 3] bajo la suma 14 a través de pasos sucesivos: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce()generaliza esta idea. Aplica una operación binaria a un iterable hasta que solo queda el resultado.En este artículo, exploraré los elementos clave de la programación orientada a objetos (
reduce(), OOP) en Python y daré algunos ejemplos prácticos. Si necesitas refrescar tus conocimientos básicos sobre Python, te recomiendo que consultes estos recursos:Historia
Python incluye otras funciones de programación funcional en Python, como
map()yfilter().Las construcciones funcionales como
map()yfilter()estaban en Python 1.0. A Guido van Rossum no les gustaba, señalando quereduce()era difícil de analizar, y que un bucleforcasi siempre es más legible. En Python 3.0, siguiendo la PEP 3100, los programadores eliminaronreduce()como función integrada y lo trasladaron al módulofunctools. Este cambio afunctoolsbásicamente lo degradó a la categoría de herramienta especializada.¿Por qué utilizar reduce()?
La mayoría de las veces, creo que probablemente sea mejor opción utilizar un bucle o una función integrada. Sin embargo,
reduce()sigue siendo una buena opción en algunos casos de uso.Cómo funciona la función reduce() de Python
Exploremos el funcionamiento de
reduce().La firma de la función básica de
reduce()esLa función
reduce()función toma dos argumentos obligatorios y un tercero opcional.function: una función binaria que especifica cómo combinar dos elementos,iterable: la secuencia o iterable que se va a reducir, como una lista o una tupla.initializer(opcional): un valor inicial para sembrar la función.Ejemplo paso a paso:
El resultado final es 13.
Ejemplos sencillos de reduce()
Los siguientes ejemplos ilustrativos muestran cómo utilizar
reduce().Inicializador
Sin un inicializador,
reduce()toma el primer elemento del iterable como su valor inicial. Si el iterable está vacío,reduce()lanza unTypeError. Para que el código sea robusto, proporciona un inicializador para definir el comportamiento ante una entrada vacía.Los inicializadores también generan un resultado con un contenedor vacío. Por ejemplo, puedes concatenar palabras en una lista plana de caracteres.
Para explorar más a fondo Python y la manipulación de datos, aquí tienes algunas opciones que te recomiendo.
Definición del reductor
Hasta ahora, hemos utilizado funciones lambda para definir el operador binario.
También puedes utilizar operadores del módulo
operator. El módulooperatorcontiene versiones funcionales de operadores comunes y llamadas a métodos. Por ejemplo, en lugar de x + y,operator.add(x, y). Esto te permite pasar operadores predefinidos (y eficientes) areduce() sin necesidad de escribir una lambda.Una tercera opción es escribir una función personalizada. Esta es una buena opción cuando no hay un operador predefinido o la función es demasiado complicada para una lambda.
Por ejemplo, supongamos que deseas eliminar los duplicados de una lista, pero manteniendo el orden de aparición. Podrías definir un reductor que añada un elemento a una lista solo si no ha aparecido anteriormente.
Para obtener más ideas sobre cómo eliminar duplicados, consulta este tutorial.
Rendimiento de reduce() en Python
reduce()implica problemas de rendimiento.Sobrecarga de llamadas a funciones
Python
reduce()llama a nuestra función una vez por cada elemento. En una lista con un millón de elementos, esto significa un millón de llamadas a funciones, cada una de las cuales crea un marco, maneja argumentos y actualiza recuentos de referencias. Esto añade una sobrecarga significativa.Por el contrario, una función integrada como
sumrealiza una única llamada a una función C y ejecuta el millón de sumas dentro del bucle C. Esta diferencia puede hacer que las órdenes integradas sean mucho más rápidas.Problemas de localidad de caché y eficiencia de la CPU
Reduce también se ve afectado por la localidad de la caché y la eficiencia de la CPU.
Una CPU moderna puede ejecutar varios miles de millones de instrucciones por segundo, pero el acceso a la RAM es mucho más lento. Para compensar, las CPU modernas tienencachés de nivel (L1, L2, L3) que almacenan datos para un acceso rápido.
Estas cachés aprovechan dos patrones.
Por el contrario, cada paso de
reduce()implica la búsqueda de punteros para encontrar el siguiente elemento y llamadas a funciones de Python, lo que rompe la localidad y ralentiza la CPU. Las funciones integradas y vectorizadas evitan este problema ejecutándose en bucles C ajustados.Para repasar cómo escribir código Python idiomático y eficiente, consulta:
Alternativas a Python reduce()
Elementos empotrados:
reduce(). Esta ventaja en cuanto a velocidad se acentúa en entradas grandes.sum,min), por lo que su finalidad es obvia. Una llamada areduce()hace que analices la función que se está plegando.Bucles:
reduce().reduce(). Una llamada areduce()te obliga a analizar una instrucción funcional, mientras que un bucle es más Python.itertools.accumulate()La biblioteca `
itertools` de Python proporciona una colección de iteradores de alto rendimiento, como `count()`, `product()` y `combinations()`. Una función útil deitertoolsesitertools.accumulate(). Al igual quereduce(), aplica una función sobre un iterable. Sin embargo,accumulate() almacena valores intermedios del cálculo, no solo el resultado final.Por ejemplo,
La función Acumular resulta útil cuando necesitas totales acumulados o mínimos/máximos. Por ejemplo, es posible que quieras saber la temperatura máxima mes a mes.
Errores comunes al utilizar reduce()
Cuando utilices
reduce(), ten en cuenta los siguientes inconvenientes.reduce()para cuando realmente lo necesites.reduce()en situaciones en las que sería más eficiente utilizar un generador o un enfoque de streaming.operatorsiempre que puedas. Las lambdas, especialmente con operaciones no asociativas, pueden perjudicar la claridad.Mejores prácticas y directrices para reduce() en Python
Al igual que con cualquier herramienta, existen prácticas recomendadas con
reduce(). Si has decidido quereduce()es la herramienta adecuada para ti, aquí tienes algunas pautas para su uso.Diseña primero el reductor.
min, esmath.inf, y paraset union, esset(). Esto mantiene el código robusto y libre deTypeErrors.Hazlo sencillo y legible.
Documento
Prueba
Supervisar el rendimiento
Aplicaciones avanzadas y casos de uso reales para Python reduce()
Dadas las desventajas, podría parecer que
reduce()no tiene ningún valor real. Por el contrario,reduce()tiene muchos casos de uso útiles.Procesamiento de estructuras anidadas
Reduce proporciona una forma limpia de recorrer estructuras de datos anidadas, como objetos JSON, plegando una secuencia de claves en búsquedas sucesivas.
En este caso, tiene sentido utilizar
reduce(). El JSON está profundamente anidado:user → profile → address → city. En lugar de encadenar búsquedas manualmente, representa la ruta como una lista de claves. A continuación, utilizareduce(operator.getitem, path, data)para recorrerlo. Esto mantiene el código genérico, legible y reutilizable.Canales de procesamiento de datos
Reduce puede impulsar los procesos de procesamiento de datos pasando los datos por una secuencia de transformaciones. Cada función se encarga de un solo paso, y el proceso es el resultado de aplicarlas en orden. Aquí tienes un proceso de juguete que preprocesa una cadena de texto antes de introducirla en un modelo de PLN.
Gestión de errores en aplicaciones complejas
Volvamos al ejemplo de JSON anidado. En este momento, la llamada directa a
reduce(operator.getitem, …)lanza una excepciónKeyErroroTypeErrorsi falta una clave o si encuentra un elemento que no es un diccionario. Para que el código sea más seguro, define una función auxiliar que envuelvaoperator.getitemen un bloque try/except y devuelva un valor predeterminado cuando se produzca un error.Aquí tienes una posibilidad para la función auxiliar.
Ahora, cambia las búsquedas de ejemplo para usar nuestra nueva función en lugar de una sin envolver.
reduce()Transformaciones de datos en varios pasos con map() y filter()
Puedes combinar
reduce()con otras herramientas funcionales, comomap()yfilter(), para crear transformaciones de datos de varios pasos. Aquí está vuestra canalización de preprocesamiento de PNL anterior escrita de forma funcional.Las transformaciones son:
«El rápido zorro marrón está saltando sobre un tronco» → «el rápido zorro marrón está saltando sobre un tronco»
«quick brown fox jumping log» → «quick brown fox jump log»
Para profundizar en las ideas de programación funcional y vectorización, recomendamos estos artículos de DataCamp.
Integración con el ecosistema moderno de Python
Para utilizar bien
reduce(), es útil comprender cómo encaja en el ecosistema Python moderno. Profundicemos en cómo encaja junto con NumPy y pandas, cómo sustenta los sistemas paralelos y distribuidos, y cómo interactúa con herramientas modernas como los analizadores estáticos.numpy y pandas
NumPy y pandas funcionan a partir de código C optimizado, así que no dupliques su funcionalidad con
reduce(). Sin embargo,reduce()es una buena opción para los pipelines con pasos dinámicos. Por ejemplo, puedes componer muchas transformaciones NumPy en un arreglo.Marcos de computación paralela y distribuida
La reducción es fundamental en los marcos de computación paralela y distribuida. Estos sistemas funcionan dividiendo un conjunto de datos en particiones, procesando esas particiones en paralelo y combinando luego los resultados parciales en una única respuesta. El paso de «combinar» es la reducción.
Para que la reducción funcione correctamente en un clúster, asegúrate de que se cumplan las siguientes condiciones.
Si estas propiedades no se cumplen, las reducciones se vuelven lentas (porque no se pueden paralelizar de forma segura) o incorrectas (porque los resultados dependen del orden de evaluación).
Este proceso requiere una operación asociativa y una identidad clara. De lo contrario, obtendrás un código lento o resultados incorrectos.
Herramientas de análisis estático
Un analizador estático (como mypy, Pyright, ruff o bandit) es una herramienta que inspecciona el código sin ejecutarlo. Estas herramientas detectan errores, aplican reglas de estilo y comprueban la corrección tipográfica.
Los analizadores estáticos tienen dificultades con
reduce(). En el proceso de plegado, el tipo de acumulador puede diferir del tipo de elemento, y la inferencia de tipos se vuelve confusa.Considera este código.
Aunque este código funciona correctamente, un analizador estático podría señalar algunos problemas.
Para que el código resulte más claro tanto para los humanos como para los analizadores, añade indicaciones de tipo.
Ahora el reductor muestra explícitamente
List[str].str.list[str]».Con estas pistas, los analizadores estáticos pueden comprobar rigurosamente el código.
Conclusión
Reduce proviene de la programación funcional, donde la idea central es combinar colecciones en un único resultado. Aunque ya no es una herramienta de primera clase en Python, tiene su lugar. Cuando necesitas tuberías flexibles, pliegues personalizados u operaciones que no se ajustan perfectamente a las funciones existentes,
reduce()es una herramienta muy potente. Si se utiliza con cuidado, se integra perfectamente en el ecosistema Python más amplio y sigue siendo una herramienta práctica en las situaciones adecuadas.Algunos enlaces relacionados con Python que pueden resultarte útiles.