DMarlon Studio

Lección 16 · 16 de 20

itertools y functools · Python

Herramientas de la biblioteca estándar de Python: accumulate, combinations, chain, count, islice y groupby, y lru_cache, reduce y partial.

Python · itertools y functools en DMarlon Studio: el código del ejemplo y su salida en la terminal

El módulo `itertools` reúne iteradores ya hechos. accumulate(daily_sales) da los totales acumulados día a día. combinations(menu, 2) genera todas las parejas posibles de platos sin repetir ni importar el orden: con cuatro platos salen 6. chain encadena varias secuencias como si fueran una sola.

count(500, 5) cuenta sin fin (500, 505, 510…) y, como todo en itertools, es perezoso. Para no recorrerlo eternamente, `islice(tickets, 4)` toma solo los cuatro primeros, igual que un corte [:4] pero para iteradores.

`groupby` agrupa elementos consecutivos iguales y devuelve pares de clave y grupo. Por eso INFO aparece dos veces en runs: los dos no están juntos. Si quiere un grupo por valor, ordene antes con sorted().

Del módulo `functools`: reduce combina los elementos de dos en dos hasta dejar uno (aquí multiplica los descuentos sucesivos: 0,9 × 0,95 × 0,8) y `partial` crea una función nueva con un argumento ya fijado: spain(p) equivale a price_with_tax(p, rate=0.21).

`@lru_cache` guarda los resultados de una función según sus argumentos. La segunda llamada con "EU" no ejecuta el cuerpo (no sale el mensaje de cálculo) y cache_info() lo confirma: un acierto y dos fallos. Úselo solo con funciones que den siempre lo mismo para los mismos argumentos.

Código · main.py

from itertools import accumulate, chain, combinations, groupby, islice, countfrom functools import lru_cache, partial, reducedaily_sales = [120, 80, 150, 95, 200]print("Running total:", list(accumulate(daily_sales)))menu = ["soup", "salad", "pasta", "steak"]pairs = list(combinations(menu, 2))   # every pair, no repeatsprint(len(pairs), "combos, e.g.", pairs[:3])print("All orders:", list(chain(["#101", "#102"], ["#103"])))tickets = (f"T{n}" for n in count(500, 5))          # endless counterprint("Next tickets:", list(islice(tickets, 4)))    # take only 4log = ["ERROR", "ERROR", "INFO", "WARN", "WARN", "WARN", "INFO"]runs = [(level, len(list(group))) for level, group in groupby(log)]print("Runs:", runs)                  # groupby joins neighbours onlydiscounts = [0.9, 0.95, 0.8]          # 10%, then 5%, then 20% offprint("Combined factor:", round(reduce(lambda a, b: a * b, discounts), 3))def price_with_tax(price, rate):    return round(price * (1 + rate), 2)spain = partial(price_with_tax, rate=0.21)      # fix one argumentprint("With Spanish VAT:", [spain(p) for p in [19.99, 5.5, 30.0]])@lru_cache(maxsize=None)def shipping_quote(zone):    print(f"  (calculating {zone})")  # only runs the first time    return {"EU": 5.0, "US": 12.0}[zone]print("Quotes:", shipping_quote("EU"), shipping_quote("EU"), shipping_quote("US"))print("Cache:", shipping_quote.cache_info())

Salida · python3 main.py

Running total: [120, 200, 350, 445, 645]
6 combos, e.g. [('soup', 'salad'), ('soup', 'pasta'), ('soup', 'steak')]
All orders: ['#101', '#102', '#103']
Next tickets: ['T500', 'T505', 'T510', 'T515']
Runs: [('ERROR', 2), ('INFO', 1), ('WARN', 3), ('INFO', 1)]
Combined factor: 0.684
With Spanish VAT: [24.19, 6.65, 36.3]
  (calculating EU)
  (calculating US)
Quotes: 5.0 5.0 12.0
Cache: CacheInfo(hits=1, misses=2, maxsize=None, currsize=2)

Para recordar

  • accumulate, combinations y chain ahorran muchos bucles.
  • count es infinito: córtelo con islice.
  • groupby solo junta elementos consecutivos.
  • reduce combina una lista en un solo valor; partial fija argumentos.
  • @lru_cache recuerda resultados de funciones puras.

Ejercicio

Use groupby sobre sorted(log) para contar cuántas veces aparece cada nivel en total, y accumulate con max para ver, día a día, la mejor venta hasta ese momento.