Constantes y funciones puras en Python: como hacerlo bien

Bajo el paradigma de la programación funcional, las funciones deberían ser puras: el mismo input produce siempre el mismo output, sin efectos secundarios. Pero en ocasiones la teoría puede chocar contra la realidad. Cuando usas constantes empiezas a depender del scope. Hay variables externas a la función. Las constantes, por definición, no rompen la pureza ya que son... constantes. El resultado es predecible. Sin embargo dificultamos el testing, ya que debemos importarlas o hacer mocks, el código empieza a ser menos legible, se vuelve menos portable y además añadimos un elemento de fragilidad ya que no podemos controlar su valor o la localización de esos valores externos.

Un caso de lo mencionado sería:

IMPUESTO = 0.21

def calcular_total(precio: float) -> float:
    return precio + (precio * IMPUESTO)

¿Qué pasa si el impuesto cambia entre países? ¿Y si donde llamo calcular_total ya tiene su propio IMPUESTO?

Te voy a dar algunas soluciones.

Inyección de dependencias con functools.partial

Puedes usar la aplicación parcial con functools.partial. Las funciones base reciben todo por parámetro y luego "fijamos" las constantes para crear las versiones definitivas.

from functools import partial


def calcular_total(impuesto: float, precio: float) -> float:
    return precio + (precio * impuesto)


def aplicar_descuento(descuento: float, precio: float) -> float:
    return precio - descuento


IMPUESTO = 0.21
DESCUENTO_BASE = 5.0

calcular_total_espania = partial(calcular_total, IMPUESTO)
aplicar_descuento_fijo = partial(aplicar_descuento, DESCUENTO_BASE)

print(calcular_total_espania(100))  # 121.0

Aquí ganamos máxima pureza. Las funciones base son cien por cien reutilizables, fáciles de testear (solo pasas configuraciones distintas por argumento) y están aisladas del entorno.

El coste es algo de complejidad visual si tu equipo no está habituado a partial.

Clausuras o closures

Otra forma es encapsular las funciones en una función constructora, una especie de factory.

def crear_sistema_precios(impuesto: float, descuento: float):

    def calcular_total(precio: float) -> float:
        return precio + (precio * impuesto)

    def aplicar_descuento(precio: float) -> float:
        return precio - descuento

    def procesar_factura(precio: float) -> float:
        return aplicar_descuento(calcular_total(precio))

    return calcular_total, aplicar_descuento, procesar_factura


calcular, descontar, procesar = crear_sistema_precios(0.21, 5.0)

Las constantes quedan atrapadas en el closure, y agrupamos de forma lógica las funciones que comparten contexto sin recurrir a clases, evitando así el estado mutable.

La contrapartida es que puede dificultar la lectura si las funciones internas crecen demasiado.

Conclusiones

Python es multiparadigma, así que no estás obligado a casarte con un solo enfoque. Lo importante es ser consciente del compromiso: cuanto más hacia afuera mira una función, más cómoda es de escribir, pero más difícil de testear de forma aislada.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Will you buy me a coffee?

Comments

There are no comments yet.

Written by Andros Fenollosa

June 5, 2026

2 min of reading

You may also like

Visitors in real time

You are alone: 🐱