DMarlon Studio

Lección 15 · 15 de 20

Counter, defaultdict, deque y namedtuple · Python

Las estructuras de datos del módulo collections de Python: Counter, defaultdict, deque y namedtuple, aplicadas a las ventas de una cafetería.

Python · Counter, defaultdict, deque y namedtuple en DMarlon Studio: el código del ejemplo y su salida en la terminal

`namedtuple` crea una tupla con nombres de campo. Sale("Ana", "coffee", 3.2) ocupa lo mismo que una tupla, es inmutable y se lee mejor: first.customer en lugar de first[0], aunque el índice sigue funcionando (first[2]).

`Counter` cuenta lo que se le dé. A partir de una expresión generadora con los productos, most_common(2) devuelve los dos más vendidos con su cuenta. Preguntar por algo que no está, como best["juice"], da 0 en vez de un KeyError. Al final se usa también como acumulador: totals[sale.customer] += sale.amount funciona aunque el cliente aún no exista.

`defaultdict(list)` es un diccionario que, cuando falta una clave, la crea con el valor de list(), una lista vacía. Así by_customer[sale.customer].append(...) agrupa las ventas por cliente sin comprobar antes si la clave existe.

`deque` (*double-ended queue*) añade y quita por los dos extremos con la misma rapidez. Con maxlen=3 solo guarda los tres últimos elementos: cada append nuevo expulsa el más antiguo, y appendleft("VIP") entra por la izquierda y expulsa por la derecha. Es ideal para historiales y colas.

La comprensión final redondea los totales, porque las sumas de decimales como 3.2 + 4.0 + 3.2 arrastran pequeños errores de coma flotante.

Código · main.py

from collections import Counter, defaultdict, deque, namedtupleSale = namedtuple("Sale", ["customer", "product", "amount"])sales = [    Sale("Ana", "coffee", 3.2), Sale("Luis", "tea", 2.5),    Sale("Ana", "cake", 4.0), Sale("Marta", "coffee", 3.2),    Sale("Luis", "coffee", 3.2), Sale("Ana", "coffee", 3.2),]first = sales[0]print(first, "->", first.customer, first[2])      # by name or by index# Counter: count anything, then ask for the most commonbest = Counter(sale.product for sale in sales)print("Top products:", best.most_common(2))print("Cake sold:", best["cake"], "| juice sold:", best["juice"])# defaultdict: a missing key starts with list() instead of failingby_customer = defaultdict(list)for sale in sales:    by_customer[sale.customer].append(sale.product)print("Ana bought:", by_customer["Ana"])# deque: fast at both ends; maxlen keeps only the last N itemsrecent = deque(maxlen=3)for sale in sales:    recent.append(sale.customer)print("Last 3 customers:", list(recent))recent.appendleft("VIP")              # the oldest drops off the other endprint("After appendleft:", list(recent))totals = Counter()for sale in sales:    totals[sale.customer] += sale.amountprint("Spent:", {name: round(total, 2) for name, total in totals.items()})

Salida · python3 main.py

Sale(customer='Ana', product='coffee', amount=3.2) -> Ana 3.2
Top products: [('coffee', 4), ('tea', 1)]
Cake sold: 1 | juice sold: 0
Ana bought: ['coffee', 'cake', 'coffee']
Last 3 customers: ['Marta', 'Luis', 'Ana']
After appendleft: ['VIP', 'Marta', 'Luis']
Spent: {'Ana': 10.4, 'Luis': 5.7, 'Marta': 3.2}

Para recordar

  • namedtuple: una tupla con campos con nombre.
  • Counter cuenta y most_common(n) da los n primeros.
  • defaultdict(list) crea la lista vacía si falta la clave.
  • deque(maxlen=n) guarda solo los últimos n elementos.
  • Las claves que faltan en un Counter valen 0.

Ejercicio

Use un Counter para saber cuántas compras ha hecho cada cliente y escriba el cliente que más veces ha comprado.