Python

Python

Tutorial Python: Composición de funciones

Aprende la composición de funciones en Python con ejemplos prácticos y la nueva función functools.compose para crear pipelines eficientes.

Aprende Python y certifícate

Principios de composición funcional

La composición de funciones es un concepto fundamental en la programación funcional que nos permite combinar múltiples funciones para crear transformaciones más complejas. En su esencia, la composición funcional consiste en tomar la salida de una función y pasarla como entrada a otra, creando así un flujo de datos transformativo.

Imagina una cadena de producción en una fábrica: cada estación realiza una tarea específica sobre un producto que va avanzando por la línea. De manera similar, en la composición funcional cada función representa una "estación" que transforma los datos que fluyen a través de ella.

Definición matemática

Matemáticamente, la composición de funciones se representa como (f ∘ g)(x) = f(g(x)), donde primero se aplica la función g y luego se aplica f al resultado. En Python, esto se traduce de manera natural:

def g(x):
    return x * 2

def f(x):
    return x + 1

# Composición manual
resultado = f(g(5))  # Primero g(5) = 10, luego f(10) = 11
print(resultado)  # 11

Ventajas de la composición funcional

La composición de funciones ofrece varias ventajas significativas:

  • Modularidad: Cada función realiza una tarea específica y bien definida.
  • Reutilización: Las funciones pueden combinarse de diferentes maneras para resolver diversos problemas.
  • Legibilidad: El código expresa claramente la secuencia de transformaciones.
  • Mantenibilidad: Es más fácil probar y depurar funciones pequeñas e independientes.

Funciones puras

Para que la composición funcional sea efectiva, es recomendable trabajar con funciones puras. Una función pura:

  1. Siempre produce el mismo resultado para los mismos argumentos
  2. No tiene efectos secundarios (no modifica variables externas)
  3. No depende del estado externo
# Función pura
def incrementar(x):
    return x + 1

# Función impura (depende del estado externo)
contador = 0
def incrementar_contador():
    global contador
    contador += 1
    return contador

Las funciones puras son ideales para la composición porque su comportamiento es predecible y no interfieren entre sí.

Composición de múltiples funciones

La composición puede extenderse a más de dos funciones, creando una cadena de transformaciones:

def cuadrado(x):
    return x * x

def duplicar(x):
    return x * 2

def sumar_uno(x):
    return x + 1

# Composición de tres funciones
valor = 3
resultado = sumar_uno(duplicar(cuadrado(valor)))
print(resultado)  # 3² = 9, 9*2 = 18, 18+1 = 19

Esta forma de anidar llamadas a funciones puede volverse difícil de leer cuando hay muchas transformaciones. Podemos mejorar la legibilidad utilizando variables intermedias:

valor = 3
paso1 = cuadrado(valor)    # 9
paso2 = duplicar(paso1)    # 18
resultado = sumar_uno(paso2)  # 19

Orden de aplicación

Es importante entender que en la composición funcional, las funciones se aplican de adentro hacia afuera. Esto puede resultar confuso al leer el código, ya que leemos de izquierda a derecha, pero las funciones se ejecutan en orden inverso:

# En esta expresión:
resultado = f(g(h(x)))

# El orden de ejecución es:
# 1. h(x)
# 2. g(resultado_de_h)
# 3. f(resultado_de_g)

Aplicación parcial y currificación

La aplicación parcial y la currificación son técnicas que facilitan la composición funcional:

from functools import partial

def multiplicar(a, b):
    return a * b

# Aplicación parcial: creamos una nueva función que multiplica por 2
duplicar = partial(multiplicar, 2)

# Ahora podemos usar esta función en composiciones
resultado = duplicar(5)  # 10

Ejemplo práctico: procesamiento de datos

Veamos un ejemplo práctico de composición funcional para procesar una lista de números:

def filtrar_pares(numeros):
    return [n for n in numeros if n % 2 == 0]

def cuadrados(numeros):
    return [n * n for n in numeros]

def suma_total(numeros):
    return sum(numeros)

# Composición para calcular la suma de los cuadrados de números pares
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
resultado = suma_total(cuadrados(filtrar_pares(numeros)))
print(resultado)  # 2² + 4² + 6² + 8² + 10² = 4 + 16 + 36 + 64 + 100 = 220

Este ejemplo muestra cómo podemos componer tres operaciones distintas (filtrado, transformación y reducción) para crear un procesamiento de datos más complejo.

Composición con funciones de orden superior

Las funciones de orden superior (funciones que toman o devuelven otras funciones) son herramientas poderosas para la composición funcional:

def componer_dos(f, g):
    """Compone dos funciones: f(g(x))"""
    def composicion(x):
        return f(g(x))
    return composicion

# Creamos funciones simples
sumar_cinco = lambda x: x + 5
multiplicar_por_tres = lambda x: x * 3

# Componemos las funciones
f_compuesta = componer_dos(sumar_cinco, multiplicar_por_tres)

# Usamos la función compuesta
resultado = f_compuesta(4)  # 4 * 3 = 12, 12 + 5 = 17
print(resultado)  # 17

Esta función componer_dos nos permite crear una nueva función que representa la composición de otras dos, lo que facilita la reutilización y la legibilidad.

Composición generalizada

Podemos generalizar la composición para trabajar con un número arbitrario de funciones:

def componer(*funciones):
    """
    Compone múltiples funciones de derecha a izquierda.
    componer(f, g, h)(x) es equivalente a f(g(h(x)))
    """
    def composicion(x):
        resultado = x
        # Aplicamos las funciones en orden inverso
        for f in reversed(funciones):
            resultado = f(resultado)
        return resultado
    return composicion

# Definimos funciones simples
sumar_uno = lambda x: x + 1
duplicar = lambda x: x * 2
cuadrado = lambda x: x * x

# Componemos las funciones
pipeline = componer(sumar_uno, duplicar, cuadrado)

# Usamos la composición
resultado = pipeline(3)  # 3² = 9, 9*2 = 18, 18+1 = 19
print(resultado)  # 19

Esta implementación de componer nos permite crear "pipelines" de procesamiento de datos de forma clara y concisa.

Implementación de composición en Python

Ahora que comprendemos los principios teóricos de la composición funcional, vamos a explorar diferentes formas de implementarla en Python. El lenguaje ofrece varias técnicas para crear composiciones de funciones de manera elegante y eficiente.

Implementación básica con funciones anidadas

La forma más directa de implementar composición en Python es mediante el anidamiento de llamadas a funciones:

def duplicar(x):
    return x * 2

def incrementar(x):
    return x + 1

# Composición mediante anidamiento
resultado = incrementar(duplicar(5))  # 5 → 10 → 11
print(resultado)  # 11

Sin embargo, este enfoque se vuelve difícil de leer cuando tenemos muchas funciones. Podemos mejorar esto creando una función auxiliar que implemente la composición:

def componer_dos(f, g):
    """Crea una nueva función que aplica f después de g"""
    return lambda x: f(g(x))

# Creamos una función compuesta
incrementar_duplicado = componer_dos(incrementar, duplicar)

# La usamos como cualquier otra función
resultado = incrementar_duplicado(5)  # 11
print(resultado)

Composición con múltiples funciones

Podemos extender nuestra implementación para manejar un número arbitrario de funciones:

def componer(*funciones):
    """
    Compone varias funciones, de derecha a izquierda.
    componer(f, g, h)(x) equivale a f(g(h(x)))
    """
    if not funciones:
        return lambda x: x  # Función identidad si no hay funciones
    
    def composicion(x):
        resultado = x
        for f in reversed(funciones):  # Aplicamos de derecha a izquierda
            resultado = f(resultado)
        return resultado
    
    return composicion

# Definimos algunas funciones simples
cuadrado = lambda x: x * x
duplicar = lambda x: x * 2
incrementar = lambda x: x + 1

# Componemos las tres funciones
pipeline = componer(incrementar, duplicar, cuadrado)

# Usamos la función compuesta
valor = 3
resultado = pipeline(valor)  # 3² = 9, 9*2 = 18, 18+1 = 19
print(resultado)  # 19

Esta implementación nos permite crear pipelines de procesamiento de manera clara y concisa.

Composición con operador de tubería

Python no tiene un operador de tubería nativo como otros lenguajes funcionales (por ejemplo, el operador |> en F# o Elixir), pero podemos simular uno usando clases:

class Pipe:
    """Clase para simular un operador de tubería"""
    def __init__(self, valor):
        self.valor = valor
    
    def __or__(self, funcion):
        """Implementa el operador | para aplicar funciones"""
        return Pipe(funcion(self.valor))
    
    def valor_final(self):
        """Devuelve el valor resultante"""
        return self.valor

# Uso del operador de tubería simulado
resultado = (
    Pipe(3)
    | cuadrado    # 9
    | duplicar    # 18
    | incrementar # 19
).valor_final()

print(resultado)  # 19

Este enfoque permite escribir composiciones de funciones de manera más legible, siguiendo un flujo de izquierda a derecha que refleja mejor el orden de las transformaciones.

Composición con reducción

Otra forma elegante de implementar la composición es utilizando functools.reduce:

from functools import reduce

def componer_con_reduce(*funciones):
    """Compone funciones usando reduce"""
    def componer_dos(f, g):
        return lambda x: f(g(x))
    
    # Si no hay funciones, devolvemos la función identidad
    if not funciones:
        return lambda x: x
    
    # Componemos las funciones de derecha a izquierda
    return reduce(componer_dos, funciones)

# Creamos una composición
pipeline = componer_con_reduce(incrementar, duplicar, cuadrado)

# La utilizamos
resultado = pipeline(3)  # 19
print(resultado)

Composición con decoradores

Los decoradores de Python también pueden utilizarse para implementar composición de funciones:

def aplicar_despues(g):
    """Decorador que aplica g después de la función decorada"""
    def decorador(f):
        def funcion_compuesta(x):
            return g(f(x))
        return funcion_compuesta
    return decorador

# Usamos decoradores para componer funciones
@aplicar_despues(incrementar)
@aplicar_despues(duplicar)
def calcular(x):
    return x * x

# Esta definición equivale a: incrementar(duplicar(calcular(x)))
resultado = calcular(3)  # 19
print(resultado)

Composición con listas de comprensión

Para operaciones sobre colecciones, podemos usar listas de comprensión anidadas como forma de composición:

numeros = [1, 2, 3, 4, 5]

# Composición mediante listas de comprensión anidadas
resultado = sum([n * 2 for n in [x * x for x in numeros if x % 2 == 0]])
print(resultado)  # 40 (2² * 2 + 4² * 2 = 8 + 32 = 40)

Sin embargo, este enfoque puede volverse difícil de leer. Una alternativa más clara es usar funciones intermedias:

def filtrar_pares(nums):
    return [n for n in nums if n % 2 == 0]

def calcular_cuadrados(nums):
    return [n * n for n in nums]

def duplicar_todos(nums):
    return [n * 2 for n in nums]

# Composición más legible
resultado = sum(duplicar_todos(calcular_cuadrados(filtrar_pares(numeros))))
print(resultado)  # 40

Composición con generadores

Los generadores ofrecen una forma eficiente de implementar composición para procesamiento de datos, especialmente para conjuntos grandes:

def filtrar_pares_gen(nums):
    for n in nums:
        if n % 2 == 0:
            yield n

def calcular_cuadrados_gen(nums):
    for n in nums:
        yield n * n

def duplicar_todos_gen(nums):
    for n in nums:
        yield n * 2

# Composición con generadores
numeros = range(1, 6)  # 1, 2, 3, 4, 5
pipeline = duplicar_todos_gen(calcular_cuadrados_gen(filtrar_pares_gen(numeros)))
resultado = sum(pipeline)
print(resultado)  # 40

Esta implementación es más eficiente en memoria porque procesa los elementos uno por uno, sin crear listas intermedias.

Composición con map y filter

Las funciones map y filter son herramientas fundamentales para la programación funcional en Python y se prestan naturalmente a la composición:

numeros = [1, 2, 3, 4, 5]

# Composición con map y filter
pares = filter(lambda x: x % 2 == 0, numeros)
cuadrados = map(lambda x: x * x, pares)
duplicados = map(lambda x: x * 2, cuadrados)
resultado = sum(duplicados)
print(resultado)  # 40

Composición con funciones de orden superior

Podemos crear funciones de orden superior que faciliten patrones comunes de composición:

def componer_transformaciones(transformaciones):
    """Compone una serie de transformaciones sobre colecciones"""
    def aplicar(datos):
        resultado = datos
        for transformacion in transformaciones:
            resultado = transformacion(resultado)
        return resultado
    return aplicar

# Definimos transformaciones
filtrar = lambda datos: filter(lambda x: x % 2 == 0, datos)
elevar_cuadrado = lambda datos: map(lambda x: x * x, datos)
duplicar = lambda datos: map(lambda x: x * 2, datos)

# Componemos las transformaciones
pipeline = componer_transformaciones([filtrar, elevar_cuadrado, duplicar])

# Aplicamos la composición
numeros = [1, 2, 3, 4, 5]
resultado = sum(pipeline(numeros))
print(resultado)  # 40

Composición con clases

También podemos implementar la composición utilizando clases, lo que puede ser útil para crear pipelines más complejos:

class Transformacion:
    """Clase base para transformaciones componibles"""
    def __init__(self, siguiente=None):
        self.siguiente = siguiente
    
    def procesar(self, datos):
        resultado = self._aplicar(datos)
        if self.siguiente:
            return self.siguiente.procesar(resultado)
        return resultado
    
    def _aplicar(self, datos):
        """Método a implementar por las subclases"""
        return datos
    
    def __rshift__(self, otra):
        """Implementa el operador >> para componer transformaciones"""
        if self.siguiente:
            self.siguiente >> otra
        else:
            self.siguiente = otra
        return self

# Implementamos transformaciones específicas
class FiltrarPares(Transformacion):
    def _aplicar(self, datos):
        return [x for x in datos if x % 2 == 0]

class CalcularCuadrados(Transformacion):
    def _aplicar(self, datos):
        return [x * x for x in datos]

class Duplicar(Transformacion):
    def _aplicar(self, datos):
        return [x * 2 for x in datos]

# Componemos las transformaciones
pipeline = FiltrarPares() >> CalcularCuadrados() >> Duplicar()

# Aplicamos la composición
numeros = [1, 2, 3, 4, 5]
resultado = sum(pipeline.procesar(numeros))
print(resultado)  # 40

Este enfoque orientado a objetos es más verboso pero ofrece mayor flexibilidad para implementar transformaciones complejas y mantener estado entre etapas del pipeline.

Composición con diccionarios de configuración

Para casos donde necesitamos configurar dinámicamente nuestras composiciones:

def crear_pipeline(configuracion):
    """Crea un pipeline basado en una configuración"""
    transformaciones = []
    
    if configuracion.get('filtrar_pares', False):
        transformaciones.append(lambda datos: [x for x in datos if x % 2 == 0])
    
    if configuracion.get('calcular_cuadrados', False):
        transformaciones.append(lambda datos: [x * x for x in datos])
    
    if configuracion.get('duplicar', False):
        transformaciones.append(lambda datos: [x * 2 for x in datos])
    
    def pipeline(datos):
        resultado = datos
        for t in transformaciones:
            resultado = t(resultado)
        return resultado
    
    return pipeline

# Configuramos y creamos el pipeline
config = {
    'filtrar_pares': True,
    'calcular_cuadrados': True,
    'duplicar': True
}

pipeline = crear_pipeline(config)
numeros = [1, 2, 3, 4, 5]
resultado = sum(pipeline(numeros))
print(resultado)  # 40

Esta técnica es útil para sistemas configurables donde las transformaciones pueden variar según las necesidades.

functools.compose

A partir de Python 3.13, el módulo functools incorpora una nueva función llamada compose que simplifica enormemente la implementación de la composición funcional. Esta adición oficial al lenguaje reconoce la importancia de la composición de funciones como un patrón fundamental en la programación funcional.

La función compose permite combinar múltiples funciones en una sola operación, aplicándolas de derecha a izquierda, siguiendo la notación matemática tradicional de composición (f ∘ g)(x) = f(g(x)).

Sintaxis básica

La sintaxis de functools.compose es simple y directa:

from functools import compose

# Definimos funciones simples
def duplicar(x):
    return x * 2

def sumar_uno(x):
    return x + 1

# Componemos las funciones
f_compuesta = compose(sumar_uno, duplicar)

# Usamos la función compuesta
resultado = f_compuesta(5)  # 5 → 10 → 11
print(resultado)  # 11

En este ejemplo, compose(sumar_uno, duplicar) crea una nueva función que primero aplica duplicar y luego sumar_uno al resultado.

Orden de aplicación

Es importante entender que compose aplica las funciones de derecha a izquierda, lo que puede resultar contraintuitivo para programadores acostumbrados a leer código de izquierda a derecha:

from functools import compose

def cuadrado(x):
    return x * x

def duplicar(x):
    return x * 2

def sumar_uno(x):
    return x + 1

# En esta composición:
f = compose(sumar_uno, duplicar, cuadrado)

# El orden de aplicación es:
# 1. cuadrado (la función más a la derecha)
# 2. duplicar
# 3. sumar_uno (la función más a la izquierda)

resultado = f(3)  # 3² = 9, 9*2 = 18, 18+1 = 19
print(resultado)  # 19

Este orden sigue la notación matemática estándar para la composición de funciones, donde (f ∘ g ∘ h)(x) = f(g(h(x))).

Ventajas sobre implementaciones manuales

Aunque en secciones anteriores vimos cómo implementar la composición manualmente, functools.compose ofrece varias ventajas:

  • Código más conciso: No necesitamos escribir nuestra propia función de composición.
  • Optimización interna: La implementación oficial está optimizada para rendimiento.
  • Claridad de intención: El uso de una función estándar comunica claramente la intención del código.
  • Mantenibilidad: Dependemos de la biblioteca estándar en lugar de código personalizado.

Composición con funciones de múltiples argumentos

compose funciona naturalmente con funciones que toman un solo argumento, pero ¿qué ocurre con funciones que requieren múltiples parámetros? Podemos usar técnicas como la aplicación parcial o funciones lambda:

from functools import compose, partial

def multiplicar(a, b):
    return a * b

def sumar(a, b):
    return a + b

# Aplicación parcial para crear funciones de un solo argumento
multiplicar_por_2 = partial(multiplicar, 2)
sumar_3 = partial(sumar, 3)

# Ahora podemos componer estas funciones
pipeline = compose(sumar_3, multiplicar_por_2)

resultado = pipeline(5)  # 5*2 = 10, 10+3 = 13
print(resultado)  # 13

Uso con funciones de procesamiento de colecciones

compose es particularmente útil cuando trabajamos con funciones que procesan colecciones de datos:

from functools import compose

def filtrar_pares(numeros):
    return [n for n in numeros if n % 2 == 0]

def calcular_cuadrados(numeros):
    return [n * n for n in numeros]

def sumar_lista(numeros):
    return sum(numeros)

# Componemos las funciones para crear un pipeline de procesamiento
pipeline = compose(sumar_lista, calcular_cuadrados, filtrar_pares)

# Aplicamos el pipeline a una lista de números
numeros = [1, 2, 3, 4, 5, 6]
resultado = pipeline(numeros)  # Filtra [2,4,6], calcula [4,16,36], suma 56
print(resultado)  # 56

Este patrón es muy común en el procesamiento de datos, donde necesitamos aplicar varias transformaciones secuenciales a un conjunto de datos.

Combinación con otras funciones de functools

compose se integra perfectamente con otras funciones del módulo functools, creando combinaciones potentes:

from functools import compose, partial, reduce

# Función que aplica una operación a todos los elementos
def map_operacion(operacion, lista):
    return [operacion(x) for x in lista]

# Creamos operaciones parciales
duplicar_lista = partial(map_operacion, lambda x: x * 2)
elevar_al_cuadrado = partial(map_operacion, lambda x: x * x)

# Función para filtrar elementos
def solo_mayores_que(umbral, lista):
    return [x for x in lista if x > umbral]

filtrar_mayores_que_10 = partial(solo_mayores_que, 10)

# Componemos las operaciones
pipeline = compose(sum, filtrar_mayores_que_10, duplicar_lista, elevar_al_cuadrado)

# Aplicamos el pipeline
numeros = [1, 2, 3, 4, 5]
resultado = pipeline(numeros)  # [1,4,9,16,25] → [2,8,18,32,50] → [18,32,50] → 100
print(resultado)  # 100

Ejemplo práctico: procesamiento de texto

Veamos un ejemplo más práctico de procesamiento de texto utilizando compose:

from functools import compose
import re

def normalizar_texto(texto):
    """Convierte a minúsculas y elimina caracteres especiales"""
    return re.sub(r'[^\w\s]', '', texto.lower())

def tokenizar(texto):
    """Divide el texto en palabras"""
    return texto.split()

def eliminar_stopwords(tokens):
    """Elimina palabras comunes sin valor semántico"""
    stopwords = {'el', 'la', 'los', 'las', 'un', 'una', 'y', 'de', 'en', 'a'}
    return [token for token in tokens if token not in stopwords]

def contar_palabras(tokens):
    """Cuenta la frecuencia de cada palabra"""
    contador = {}
    for token in tokens:
        contador[token] = contador.get(token, 0) + 1
    return contador

# Componemos las funciones para crear un pipeline de procesamiento de texto
analizar_texto = compose(contar_palabras, eliminar_stopwords, tokenizar, normalizar_texto)

# Analizamos un texto
texto = "El análisis de texto es una técnica importante en el procesamiento del lenguaje natural."
resultado = analizar_texto(texto)
print(resultado)
# {'análisis': 1, 'texto': 1, 'es': 1, 'técnica': 1, 'importante': 1, 'procesamiento': 1, 'del': 1, 'lenguaje': 1, 'natural': 1}

Este ejemplo muestra cómo podemos crear un pipeline de procesamiento de texto completo utilizando compose, donde cada función realiza una transformación específica y bien definida.

Creación de pipelines de datos

functools.compose es ideal para crear pipelines de procesamiento de datos, especialmente en análisis de datos y ciencia de datos:

from functools import compose
import statistics

def normalizar_datos(datos):
    """Normaliza los datos restando la media y dividiendo por la desviación estándar"""
    media = statistics.mean(datos)
    desviacion = statistics.stdev(datos)
    return [(x - media) / desviacion for x in datos]

def filtrar_outliers(datos, umbral=2.0):
    """Elimina valores atípicos (outliers)"""
    return [x for x in datos if abs(x) <= umbral]

def calcular_estadisticas(datos):
    """Calcula estadísticas básicas de los datos"""
    return {
        'media': statistics.mean(datos),
        'mediana': statistics.median(datos),
        'desviacion': statistics.stdev(datos) if len(datos) > 1 else 0,
        'min': min(datos),
        'max': max(datos)
    }

# Creamos un pipeline para procesar datos numéricos
pipeline_estadistico = compose(
    calcular_estadisticas,
    lambda datos: filtrar_outliers(datos, 1.5),  # Usamos lambda para pasar parámetros adicionales
    normalizar_datos
)

# Aplicamos el pipeline a un conjunto de datos
datos = [12, 15, 18, 22, 30, 31, 35, 40, 41, 100]  # 100 es un outlier
resultado = pipeline_estadistico(datos)
print(resultado)
# Muestra estadísticas de los datos normalizados sin outliers

Limitaciones y consideraciones

Aunque functools.compose es una herramienta poderosa, tiene algunas limitaciones a tener en cuenta:

  • Disponibilidad: Solo está disponible a partir de Python 3.13, por lo que en versiones anteriores necesitarás implementar tu propia función de composición.
  • Depuración: Puede ser más difícil depurar errores en funciones compuestas, ya que no es inmediatamente obvio en qué etapa del pipeline ocurrió un error.
  • Orden inverso: El orden de aplicación de derecha a izquierda puede resultar confuso para algunos programadores.

Para mitigar estas limitaciones, considera:

from functools import compose
import traceback

def funcion_segura(f):
    """Decorador que captura excepciones y proporciona información de depuración"""
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            print(f"Error en {f.__name__}: {e}")
            traceback.print_exc()
            raise
    return wrapper

# Aplicamos el decorador a nuestras funciones
duplicar_seguro = funcion_segura(lambda x: x * 2)
dividir_seguro = funcion_segura(lambda x: 10 / x)

# Componemos las funciones con manejo de errores
pipeline = compose(duplicar_seguro, dividir_seguro)

try:
    resultado = pipeline(0)  # Causará un error de división por cero
except:
    print("El pipeline falló, pero sabemos dónde")

Alternativa para versiones anteriores de Python

Si estás trabajando con una versión de Python anterior a la 3.13, puedes implementar tu propia versión de compose:

def compose(*funciones):
    """
    Implementación de compose para versiones anteriores de Python.
    Compone funciones de derecha a izquierda.
    """
    if not funciones:
        return lambda x: x  # Función identidad
    
    def composicion(x):
        resultado = x
        for f in reversed(funciones):
            resultado = f(resultado)
        return resultado
    
    return composicion

# Uso similar a functools.compose
duplicar = lambda x: x * 2
sumar_uno = lambda x: x + 1
f = compose(sumar_uno, duplicar)

resultado = f(5)  # 11
print(resultado)

Esta implementación proporciona una funcionalidad similar a functools.compose y puede usarse como alternativa en versiones anteriores de Python.

Conclusión práctica

functools.compose representa un paso importante en la evolución de Python hacia un mejor soporte para patrones de programación funcional. Al proporcionar una implementación estándar de composición de funciones, Python facilita la creación de código más modular, legible y mantenible, especialmente para operaciones de procesamiento de datos en cadena.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende Python online

Ejercicios de esta lección Composición de funciones

Evalúa tus conocimientos de esta lección Composición de funciones con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Módulo math

Python
Puzzle

Reto herencia

Python
Código

Excepciones

Python
Test

Introducción a Python

Python
Test

Reto variables

Python
Código

Funciones Python

Python
Puzzle

Reto funciones

Python
Código

Módulo datetime

Python
Test

Reto acumulación

Python
Código

Reto estructuras condicionales

Python
Código

Polimorfismo

Python
Test

Módulo os

Python
Test

Reto métodos dunder

Python
Código

Diccionarios

Python
Puzzle

Reto clases y objetos

Python
Código

Reto operadores

Python
Código

Operadores

Python
Test

Estructuras de control

Python
Puzzle

Funciones lambda

Python
Test

Reto diccionarios

Python
Código

Reto función lambda

Python
Código

Encapsulación

Python
Puzzle

Reto coleciones

Python
Proyecto

Reto funciones auxiliares

Python
Código

Crear módulos y paquetes

Python
Puzzle

Módulo datetime

Python
Puzzle

Excepciones

Python
Puzzle

Operadores

Python
Puzzle

Diccionarios

Python
Test

Reto map, filter

Python
Código

Reto tuplas

Python
Código

Proyecto gestor de tareas CRUD

Python
Proyecto

Tuplas

Python
Puzzle

Variables

Python
Puzzle

Tipos de datos

Python
Puzzle

Conjuntos

Python
Test

Reto mixins

Python
Código

Módulo csv

Python
Test

Módulo json

Python
Test

Herencia

Python
Test

Análisis de datos de ventas con Pandas

Python
Proyecto

Reto fechas y tiempo

Python
Proyecto

Reto estructuras de iteración

Python
Código

Funciones

Python
Test

Reto comprehensions

Python
Código

Variables

Python
Test

Reto serialización

Python
Proyecto

Módulo csv

Python
Puzzle

Reto polimorfismo

Python
Código

Polimorfismo

Python
Puzzle

Clases y objetos

Python
Código

Reto encapsulación

Python
Código

Estructuras de control

Python
Test

Importar módulos y paquetes

Python
Test

Módulo math

Python
Test

Funciones lambda

Python
Puzzle

Reto excepciones

Python
Código

Listas

Python
Puzzle

Reto archivos

Python
Proyecto

Encapsulación

Python
Test

Reto conjuntos

Python
Código

Clases y objetos

Python
Test

Instalación de Python y creación de proyecto

Python
Test

Reto listas

Python
Código

Tipos de datos

Python
Test

Crear módulos y paquetes

Python
Test

Tuplas

Python
Test

Herencia

Python
Puzzle

Reto acceso a sistema

Python
Proyecto

Proyecto sintaxis calculadora

Python
Proyecto

Importar módulos y paquetes

Python
Puzzle

Clases y objetos

Python
Puzzle

Módulo os

Python
Puzzle

Listas

Python
Test

Conjuntos

Python
Puzzle

Reto tipos de datos

Python
Código

Reto matemáticas

Python
Proyecto

Módulo json

Python
Puzzle

Todas las lecciones de Python

Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Python

Python

Introducción

Instalación Y Creación De Proyecto

Python

Introducción

Tema 2: Tipos De Datos, Variables Y Operadores

Python

Introducción

Instalación De Python

Python

Introducción

Tipos De Datos

Python

Sintaxis

Variables

Python

Sintaxis

Operadores

Python

Sintaxis

Estructuras De Control

Python

Sintaxis

Funciones

Python

Sintaxis

Estructuras Control Iterativo

Python

Sintaxis

Estructuras Control Condicional

Python

Sintaxis

Testing Con Pytest

Python

Sintaxis

Listas

Python

Estructuras De Datos

Tuplas

Python

Estructuras De Datos

Diccionarios

Python

Estructuras De Datos

Conjuntos

Python

Estructuras De Datos

Comprehensions

Python

Estructuras De Datos

Clases Y Objetos

Python

Programación Orientada A Objetos

Excepciones

Python

Programación Orientada A Objetos

Encapsulación

Python

Programación Orientada A Objetos

Herencia

Python

Programación Orientada A Objetos

Polimorfismo

Python

Programación Orientada A Objetos

Mixins Y Herencia Múltiple

Python

Programación Orientada A Objetos

Métodos Especiales (Dunder Methods)

Python

Programación Orientada A Objetos

Composición De Clases

Python

Programación Orientada A Objetos

Funciones Lambda

Python

Programación Funcional

Aplicación Parcial

Python

Programación Funcional

Entrada Y Salida, Manejo De Archivos

Python

Programación Funcional

Decoradores

Python

Programación Funcional

Generadores

Python

Programación Funcional

Paradigma Funcional

Python

Programación Funcional

Composición De Funciones

Python

Programación Funcional

Funciones Orden Superior Map Y Filter

Python

Programación Funcional

Funciones Auxiliares

Python

Programación Funcional

Reducción Y Acumulación

Python

Programación Funcional

Archivos Comprimidos

Python

Entrada Y Salida Io

Entrada Y Salida Avanzada

Python

Entrada Y Salida Io

Archivos Temporales

Python

Entrada Y Salida Io

Contexto With

Python

Entrada Y Salida Io

Módulo Csv

Python

Biblioteca Estándar

Módulo Json

Python

Biblioteca Estándar

Módulo Datetime

Python

Biblioteca Estándar

Módulo Math

Python

Biblioteca Estándar

Módulo Os

Python

Biblioteca Estándar

Módulo Re

Python

Biblioteca Estándar

Módulo Random

Python

Biblioteca Estándar

Módulo Time

Python

Biblioteca Estándar

Módulo Collections

Python

Biblioteca Estándar

Módulo Sys

Python

Biblioteca Estándar

Módulo Statistics

Python

Biblioteca Estándar

Módulo Pickle

Python

Biblioteca Estándar

Módulo Pathlib

Python

Biblioteca Estándar

Importar Módulos Y Paquetes

Python

Paquetes Y Módulos

Crear Módulos Y Paquetes

Python

Paquetes Y Módulos

Entornos Virtuales (Virtualenv, Venv)

Python

Entorno Y Dependencias

Gestión De Dependencias (Pip, Requirements.txt)

Python

Entorno Y Dependencias

Python-dotenv Y Variables De Entorno

Python

Entorno Y Dependencias

Acceso A Datos Con Mysql, Pymongo Y Pandas

Python

Acceso A Bases De Datos

Acceso A Mongodb Con Pymongo

Python

Acceso A Bases De Datos

Acceso A Mysql Con Mysql Connector

Python

Acceso A Bases De Datos

Novedades Python 3.13

Python

Características Modernas

Operador Walrus

Python

Características Modernas

Pattern Matching

Python

Características Modernas

Instalación Beautiful Soup

Python

Web Scraping

Sintaxis General De Beautiful Soup

Python

Web Scraping

Tipos De Selectores

Python

Web Scraping

Web Scraping De Html

Python

Web Scraping

Web Scraping Para Ciencia De Datos

Python

Web Scraping

Autenticación Y Acceso A Recursos Protegidos

Python

Web Scraping

Combinación De Selenium Con Beautiful Soup

Python

Web Scraping

Accede GRATIS a Python y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el concepto y principios de la composición funcional.
  • Aprender a implementar composición de funciones en Python de diversas formas.
  • Conocer las ventajas de usar funciones puras y composición para mejorar modularidad y legibilidad.
  • Utilizar functools.compose para simplificar la composición a partir de Python 3.13.
  • Aplicar composición funcional en ejemplos prácticos de procesamiento de datos y texto.