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.

`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.Countercuenta ymost_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
Countervalen0.
Ejercicio
Use un Counter para saber cuántas compras ha hecho cada cliente y escriba el cliente que más veces ha comprado.