Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Patrones Comunes: Recetas Probadas para Problemas Frecuentes

🧭 Navegación:

¡Bienvenido al departamento de soluciones eficientes de tu almacén! Ahora que conoces todas las herramientas de control de flujo, es momento de aprender algunos patrones comunes que los programadores experimentados utilizan para resolver problemas frecuentes.

Los patrones comunes son como recetas probadas que combinan condicionales y bucles para resolver tareas específicas de manera eficiente y elegante.

El libro de recetas de tu almacén 📚

Imagínate que tienes un libro de recetas con soluciones optimizadas para las tareas más comunes en tu almacén:

  • Recetas de búsqueda: Encontrar elementos específicos
  • Recetas de filtrado: Seleccionar elementos que cumplan ciertos criterios
  • Recetas de transformación: Modificar elementos de forma sistemática
  • Recetas de acumulación: Combinar elementos para obtener resultados
  • Recetas de validación: Verificar que los datos cumplan ciertos requisitos
# Una receta en acción
numeros = [10, 25, 3, 8, 42, 15, 7]

# Receta para encontrar el máximo
maximo = numeros[0]  # Empezamos asumiendo que el primero es el máximo
for numero in numeros[1:]:  # Revisamos el resto
    if numero > maximo:
        maximo = numero  # Actualizamos si encontramos uno mayor

print(f"El número máximo es: {maximo}")  # 42

🔍 Mi perspectiva personal: Dominar estos patrones comunes fue un punto de inflexión en mi carrera como programador. Pasé de escribir código que “simplemente funcionaba” a escribir código elegante, eficiente y fácil de mantener. Estos patrones son como las piezas fundamentales de un juego de construcción: una vez que los dominas, puedes combinarlos para resolver problemas cada vez más complejos.

Patrón 1: Búsqueda lineal

Este patrón te permite encontrar un elemento específico en una colección:

# ================================
# BUSCADOR DE ELEMENTOS
# ================================

productos = ["laptop", "mouse", "teclado", "monitor", "auriculares"]
producto_buscado = "teclado"

print("🔍 BUSCADOR DE ELEMENTOS")
print("=" * 25)
print(f"Buscando: {producto_buscado}")
print(f"En lista: {productos}")
print()

# Inicializar variables
encontrado = False
posicion = -1

# Buscar el elemento
for i, producto in enumerate(productos):
    print(f"Revisando posición {i}: {producto}")
    
    if producto == producto_buscado:
        encontrado = True
        posicion = i
        print(f"   ✅ ¡Elemento encontrado!")
        break
    
    print("   ⏭️ Continuando búsqueda...")

# Mostrar resultado
if encontrado:
    print(f"\n🎯 {producto_buscado} encontrado en posición {posicion}")
else:
    print(f"\n❌ {producto_buscado} no encontrado en la lista")

print("🔚 Búsqueda finalizada")

Variante: Búsqueda de todos los elementos que cumplen un criterio

# ================================
# BUSCADOR DE MÚLTIPLES ELEMENTOS
# ================================

productos = ["laptop", "mouse", "teclado", "monitor", "teclado", "auriculares"]
producto_buscado = "teclado"

print("🔍 BUSCADOR DE MÚLTIPLES ELEMENTOS")
print("=" * 35)
print(f"Buscando todas las ocurrencias de: {producto_buscado}")
print(f"En lista: {productos}")
print()

# Inicializar variables
posiciones = []

# Buscar todas las ocurrencias
for i, producto in enumerate(productos):
    print(f"Revisando posición {i}: {producto}")
    
    if producto == producto_buscado:
        posiciones.append(i)
        print(f"   ✅ ¡Elemento encontrado!")
    else:
        print("   ⏭️ Continuando búsqueda...")

# Mostrar resultado
if posiciones:
    print(f"\n🎯 {producto_buscado} encontrado en {len(posiciones)} posiciones: {posiciones}")
else:
    print(f"\n❌ {producto_buscado} no encontrado en la lista")

print("🔚 Búsqueda finalizada")

Patrón 2: Filtrado

Este patrón te permite seleccionar elementos que cumplen ciertos criterios:

# ================================
# FILTRADOR DE ELEMENTOS
# ================================

productos = [
    {"nombre": "laptop", "precio": 1200, "disponible": True},
    {"nombre": "mouse", "precio": 25, "disponible": True},
    {"nombre": "teclado", "precio": 50, "disponible": False},
    {"nombre": "monitor", "precio": 300, "disponible": True},
    {"nombre": "auriculares", "precio": 80, "disponible": False}
]

print("🔍 FILTRADOR DE ELEMENTOS")
print("=" * 25)

# Filtrar productos disponibles
productos_disponibles = []

for producto in productos:
    if producto["disponible"]:
        productos_disponibles.append(producto)

print(f"Productos disponibles: {len(productos_disponibles)}")
for producto in productos_disponibles:
    print(f"   • {producto['nombre']} - ${producto['precio']}")
print()

# Filtrar productos económicos (menos de $100)
productos_economicos = []

for producto in productos:
    if producto["precio"] < 100:
        productos_economicos.append(producto)

print(f"Productos económicos: {len(productos_economicos)}")
for producto in productos_economicos:
    print(f"   • {producto['nombre']} - ${producto['precio']}")
print()

# Filtrar productos disponibles Y económicos
productos_disponibles_economicos = []

for producto in productos:
    if producto["disponible"] and producto["precio"] < 100:
        productos_disponibles_economicos.append(producto)

print(f"Productos disponibles y económicos: {len(productos_disponibles_economicos)}")
for producto in productos_disponibles_economicos:
    print(f"   • {producto['nombre']} - ${producto['precio']}")
print()

# Versión con comprensión de listas
productos_filtrados = [p for p in productos if p["disponible"] and p["precio"] < 100]
print(f"Usando comprensión de listas: {len(productos_filtrados)} productos")
for producto in productos_filtrados:
    print(f"   • {producto['nombre']} - ${producto['precio']}")

print("🔚 Filtrado finalizado")

Patrón 3: Transformación (Mapeo)

Este patrón te permite aplicar una transformación a cada elemento de una colección:

# ================================
# TRANSFORMADOR DE ELEMENTOS
# ================================

precios = [100, 25, 50, 300, 80]

print("🔄 TRANSFORMADOR DE ELEMENTOS")
print("=" * 30)
print(f"Precios originales: {precios}")
print()

# Aplicar descuento del 10%
precios_con_descuento = []

for precio in precios:
    precio_descuento = precio * 0.9  # 10% de descuento
    precios_con_descuento.append(precio_descuento)

print(f"Precios con 10% de descuento: {precios_con_descuento}")
print()

# Aplicar impuesto del 21%
precios_con_impuesto = []

for precio in precios:
    precio_impuesto = precio * 1.21  # 21% de impuesto
    precios_con_impuesto.append(precio_impuesto)

print(f"Precios con 21% de impuesto: {precios_con_impuesto}")
print()

# Versión con comprensión de listas
precios_formateados = [f"${precio:.2f}" for precio in precios]
print(f"Precios formateados: {precios_formateados}")
print()

# Transformación de objetos
productos = [
    {"nombre": "laptop", "precio": 1200},
    {"nombre": "mouse", "precio": 25},
    {"nombre": "teclado", "precio": 50}
]

# Añadir campo de precio con impuesto
for producto in productos:
    producto["precio_con_impuesto"] = producto["precio"] * 1.21

print("Productos con precio e impuesto:")
for producto in productos:
    print(f"   • {producto['nombre']}: ${producto['precio']} (con impuesto: ${producto['precio_con_impuesto']:.2f})")

print("🔚 Transformación finalizada")

Patrón 4: Acumulación

Este patrón te permite combinar elementos para obtener un resultado acumulado:

# ================================
# ACUMULADOR DE VALORES
# ================================

ventas = [1200, 300, 800, 550, 1000, 450]

print("📊 ACUMULADOR DE VALORES")
print("=" * 25)
print(f"Ventas diarias: {ventas}")
print()

# Calcular suma total
total_ventas = 0

for venta in ventas:
    total_ventas += venta

print(f"Total de ventas: ${total_ventas}")
print()

# Calcular promedio
promedio_ventas = total_ventas / len(ventas)
print(f"Promedio de ventas: ${promedio_ventas:.2f}")
print()

# Encontrar máximo y mínimo
venta_maxima = ventas[0]
venta_minima = ventas[0]
dia_maximo = 1
dia_minimo = 1

for i, venta in enumerate(ventas, 1):
    if venta > venta_maxima:
        venta_maxima = venta
        dia_maximo = i
    
    if venta < venta_minima:
        venta_minima = venta
        dia_minimo = i

print(f"Venta máxima: ${venta_maxima} (Día {dia_maximo})")
print(f"Venta mínima: ${venta_minima} (Día {dia_minimo})")
print()

# Contar ventas por encima del promedio
ventas_sobre_promedio = 0

for venta in ventas:
    if venta > promedio_ventas:
        ventas_sobre_promedio += 1

print(f"Días con ventas sobre el promedio: {ventas_sobre_promedio}")

print("🔚 Análisis finalizado")

Variante: Acumulación con diccionarios

# ================================
# CONTADOR DE FRECUENCIAS
# ================================

votos = ["A", "B", "A", "C", "B", "B", "A", "C", "A", "D", "B", "A"]

print("📊 CONTADOR DE FRECUENCIAS")
print("=" * 25)
print(f"Votos: {votos}")
print()

# Contar frecuencia de cada opción
frecuencias = {}

for voto in votos:
    if voto in frecuencias:
        frecuencias[voto] += 1
    else:
        frecuencias[voto] = 1

print("Resultados de la votación:")
for opcion, cantidad in frecuencias.items():
    print(f"   • Opción {opcion}: {cantidad} votos")
print()

# Encontrar la opción ganadora
opcion_ganadora = ""
max_votos = 0

for opcion, cantidad in frecuencias.items():
    if cantidad > max_votos:
        max_votos = cantidad
        opcion_ganadora = opcion

print(f"🏆 La opción ganadora es: {opcion_ganadora} con {max_votos} votos")

print("🔚 Conteo finalizado")

Patrón 5: Validación

Este patrón te permite verificar que los datos cumplan ciertos requisitos:

# ================================
# VALIDADOR DE DATOS
# ================================

datos_usuario = {
    "nombre": "Ana García",
    "email": "ana@ejemplo.com",
    "edad": 25,
    "contraseña": "Abc123!"
}

print("✅ VALIDADOR DE DATOS")
print("=" * 25)
print("Validando datos de usuario:")
for campo, valor in datos_usuario.items():
    print(f"   • {campo}: {valor}")
print()

# Inicializar variables
errores = []

# Validar nombre
if not datos_usuario["nombre"]:
    errores.append("El nombre no puede estar vacío")
elif len(datos_usuario["nombre"]) < 3:
    errores.append("El nombre debe tener al menos 3 caracteres")

# Validar email
email = datos_usuario["email"]
if not email:
    errores.append("El email no puede estar vacío")
elif "@" not in email or "." not in email:
    errores.append("El email debe contener '@' y '.'")

# Validar edad
edad = datos_usuario["edad"]
if not isinstance(edad, int):
    errores.append("La edad debe ser un número entero")
elif edad < 18:
    errores.append("Debes ser mayor de edad (18+)")
elif edad > 120:
    errores.append("La edad parece incorrecta")

# Validar contraseña
contraseña = datos_usuario["contraseña"]
if len(contraseña) < 6:
    errores.append("La contraseña debe tener al menos 6 caracteres")
elif not any(c.isupper() for c in contraseña):
    errores.append("La contraseña debe contener al menos una mayúscula")
elif not any(c.islower() for c in contraseña):
    errores.append("La contraseña debe contener al menos una minúscula")
elif not any(c.isdigit() for c in contraseña):
    errores.append("La contraseña debe contener al menos un número")
elif not any(c in "!@#$%^&*" for c in contraseña):
    errores.append("La contraseña debe contener al menos un carácter especial")

# Mostrar resultado
if errores:
    print("❌ Validación fallida:")
    for error in errores:
        print(f"   • {error}")
else:
    print("✅ Todos los datos son válidos")

print("🔚 Validación finalizada")

Patrón 6: Agrupación

Este patrón te permite organizar elementos en grupos según ciertos criterios:

# ================================
# AGRUPADOR DE ELEMENTOS
# ================================

productos = [
    {"nombre": "laptop", "categoria": "electrónica", "precio": 1200},
    {"nombre": "camisa", "categoria": "ropa", "precio": 25},
    {"nombre": "pantalón", "categoria": "ropa", "precio": 35},
    {"nombre": "tablet", "categoria": "electrónica", "precio": 300},
    {"nombre": "zapatos", "categoria": "ropa", "precio": 80},
    {"nombre": "monitor", "categoria": "electrónica", "precio": 250}
]

print("📊 AGRUPADOR DE ELEMENTOS")
print("=" * 25)
print(f"Total de productos: {len(productos)}")
print()

# Agrupar por categoría
productos_por_categoria = {}

for producto in productos:
    categoria = producto["categoria"]
    
    if categoria not in productos_por_categoria:
        productos_por_categoria[categoria] = []
    
    productos_por_categoria[categoria].append(producto)

# Mostrar grupos
print("Productos agrupados por categoría:")
for categoria, lista_productos in productos_por_categoria.items():
    print(f"📁 {categoria.upper()} ({len(lista_productos)} productos):")
    
    for producto in lista_productos:
        print(f"   • {producto['nombre']} - ${producto['precio']}")
    
    # Calcular precio promedio de la categoría
    precio_total = sum(p["precio"] for p in lista_productos)
    precio_promedio = precio_total / len(lista_productos)
    print(f"   📊 Precio promedio: ${precio_promedio:.2f}")
    print()

print("🔚 Agrupación finalizada")

Patrón 7: Combinación de colecciones

Este patrón te permite trabajar con múltiples colecciones relacionadas:

# ================================
# COMBINADOR DE COLECCIONES
# ================================

clientes = [
    {"id": 1, "nombre": "Ana García"},
    {"id": 2, "nombre": "Carlos López"},
    {"id": 3, "nombre": "Elena Martínez"}
]

pedidos = [
    {"cliente_id": 1, "producto": "laptop", "cantidad": 1},
    {"cliente_id": 2, "producto": "monitor", "cantidad": 2},
    {"cliente_id": 1, "producto": "mouse", "cantidad": 1},
    {"cliente_id": 3, "producto": "teclado", "cantidad": 1},
    {"cliente_id": 2, "producto": "auriculares", "cantidad": 1},
    {"cliente_id": 1, "producto": "tablet", "cantidad": 1}
]

print("🔄 COMBINADOR DE COLECCIONES")
print("=" * 30)
print(f"Clientes: {len(clientes)}")
print(f"Pedidos: {len(pedidos)}")
print()

# Crear informe de pedidos por cliente
print("INFORME DE PEDIDOS POR CLIENTE:")
print("=" * 30)

for cliente in clientes:
    cliente_id = cliente["id"]
    nombre = cliente["nombre"]
    
    print(f"👤 Cliente: {nombre}")
    
    # Encontrar todos los pedidos de este cliente
    pedidos_cliente = []
    for pedido in pedidos:
        if pedido["cliente_id"] == cliente_id:
            pedidos_cliente.append(pedido)
    
    # Mostrar pedidos
    if pedidos_cliente:
        print(f"   📦 Pedidos ({len(pedidos_cliente)}):")
        for i, pedido in enumerate(pedidos_cliente, 1):
            print(f"      {i}. {pedido['producto']} (x{pedido['cantidad']})")
    else:
        print("   ❌ No tiene pedidos")
    
    print()

print("🔚 Informe finalizado")

Patrón 8: Procesamiento por lotes

Este patrón te permite procesar grandes cantidades de datos en grupos más pequeños:

# ================================
# PROCESADOR POR LOTES
# ================================

datos = list(range(1, 101))  # Lista de números del 1 al 100
tamaño_lote = 10

print("📦 PROCESADOR POR LOTES")
print("=" * 25)
print(f"Total de elementos: {len(datos)}")
print(f"Tamaño de lote: {tamaño_lote}")
print()

# Procesar por lotes
total_lotes = (len(datos) + tamaño_lote - 1) // tamaño_lote  # Redondeo hacia arriba
for i in range(total_lotes):
    # Calcular índices del lote actual
    inicio = i * tamaño_lote
    fin = min(inicio + tamaño_lote, len(datos))
    lote_actual = datos[inicio:fin]
    
    print(f"Procesando lote {i+1}/{total_lotes} (elementos {inicio+1}-{fin}):")
    
    # Procesar cada elemento del lote
    suma_lote = 0
    for elemento in lote_actual:
        suma_lote += elemento
    
    print(f"   • Elementos: {lote_actual}")
    print(f"   • Suma del lote: {suma_lote}")
    print()

print("🔚 Procesamiento por lotes finalizado")

Comprueba tu comprensión 🧠

  1. ¿Qué patrón utilizarías para encontrar todos los productos con precio mayor a $100?

  2. ¿Cuál es la diferencia entre el patrón de transformación y el patrón de acumulación?

  3. Escribe un código que utilice el patrón de validación para verificar si una contraseña cumple con los siguientes requisitos: al menos 8 caracteres, al menos una mayúscula, al menos un número.

  4. ¿Qué patrón utilizarías para contar cuántas veces aparece cada letra en una cadena de texto?

Soluciones

  1. Para encontrar todos los productos con precio mayor a $100, utilizaría el patrón de filtrado:

    productos = [
        {"nombre": "laptop", "precio": 1200},
        {"nombre": "mouse", "precio": 25},
        {"nombre": "teclado", "precio": 50},
        {"nombre": "monitor", "precio": 300}
    ]
    
    productos_caros = []
    for producto in productos:
        if producto["precio"] > 100:
            productos_caros.append(producto)
    
    # Alternativa con comprensión de listas:
    # productos_caros = [p for p in productos if p["precio"] > 100]
    
  2. Diferencia entre transformación y acumulación:

    • Transformación (Mapeo): Aplica una operación a cada elemento de una colección para crear una nueva colección con los resultados. La cantidad de elementos de entrada y salida es la misma.
    • Acumulación: Combina todos los elementos de una colección para producir un único resultado (como una suma, promedio, máximo, etc.).
  3. Código para validar una contraseña:

    def validar_contraseña(contraseña):
        errores = []
        
        if len(contraseña) < 8:
            errores.append("La contraseña debe tener al menos 8 caracteres")
        
        if not any(c.isupper() for c in contraseña):
            errores.append("La contraseña debe contener al menos una mayúscula")
        
        if not any(c.isdigit() for c in contraseña):
            errores.append("La contraseña debe contener al menos un número")
        
        return errores
    
    # Ejemplo de uso
    contraseña = "password123"
    errores = validar_contraseña(contraseña)
    
    if errores:
        print("Contraseña inválida:")
        for error in errores:
            print(f"- {error}")
    else:
        print("Contraseña válida")
    
  4. Para contar cuántas veces aparece cada letra en una cadena de texto, utilizaría el patrón de acumulación con diccionarios (contador de frecuencias):

    texto = "programacion"
    frecuencias = {}
    
    for letra in texto:
        if letra in frecuencias:
            frecuencias[letra] += 1
        else:
            frecuencias[letra] = 1
    
    # Alternativa con defaultdict:
    # from collections import defaultdict
    # frecuencias = defaultdict(int)
    # for letra in texto:
    #     frecuencias[letra] += 1
    

Ejercicio práctico: Sistema de análisis de ventas

# ================================
# SISTEMA DE ANÁLISIS DE VENTAS
# ================================

ventas = [
    {"fecha": "2023-01-15", "producto": "laptop", "categoria": "electrónica", "cantidad": 1, "precio": 1200},
    {"fecha": "2023-01-15", "producto": "mouse", "categoria": "electrónica", "cantidad": 3, "precio": 25},
    {"fecha": "2023-01-16", "producto": "camisa", "categoria": "ropa", "cantidad": 2, "precio": 30},
    {"fecha": "2023-01-17", "producto": "laptop", "categoria": "electrónica", "cantidad": 1, "precio": 1200},
    {"fecha": "2023-01-18", "producto": "pantalón", "categoria": "ropa", "cantidad": 1, "precio": 50},
    {"fecha": "2023-01-19", "producto": "monitor", "categoria": "electrónica", "cantidad": 2, "precio": 300},
    {"fecha": "2023-01-20", "producto": "zapatos", "categoria": "ropa", "cantidad": 1, "precio": 80},
    {"fecha": "2023-01-20", "producto": "teclado", "categoria": "electrónica", "cantidad": 4, "precio": 45}
]

print("📊 SISTEMA DE ANÁLISIS DE VENTAS")
print("=" * 35)
print(f"Total de transacciones: {len(ventas)}")
print()

# 1. Calcular ventas totales (patrón de acumulación)
total_ventas = 0
total_unidades = 0

for venta in ventas:
    importe = venta["cantidad"] * venta["precio"]
    total_ventas += importe
    total_unidades += venta["cantidad"]

print(f"💰 VENTAS TOTALES: ${total_ventas:,.2f}")
print(f"📦 UNIDADES VENDIDAS: {total_unidades}")
print()

# 2. Agrupar ventas por categoría (patrón de agrupación)
ventas_por_categoria = {}

for venta in ventas:
    categoria = venta["categoria"]
    importe = venta["cantidad"] * venta["precio"]
    
    if categoria in ventas_por_categoria:
        ventas_por_categoria[categoria] += importe
    else:
        ventas_por_categoria[categoria] = importe

print("📊 VENTAS POR CATEGORÍA:")
for categoria, importe in ventas_por_categoria.items():
    porcentaje = (importe / total_ventas) * 100
    print(f"   • {categoria}: ${importe:,.2f} ({porcentaje:.1f}%)")
print()

# 3. Encontrar el producto más vendido (patrones de acumulación y búsqueda)
ventas_por_producto = {}

for venta in ventas:
    producto = venta["producto"]
    cantidad = venta["cantidad"]
    
    if producto in ventas_por_producto:
        ventas_por_producto[producto] += cantidad
    else:
        ventas_por_producto[producto] = cantidad

producto_mas_vendido = ""
max_cantidad = 0

for producto, cantidad in ventas_por_producto.items():
    if cantidad > max_cantidad:
        max_cantidad = cantidad
        producto_mas_vendido = producto

print(f"🏆 PRODUCTO MÁS VENDIDO: {producto_mas_vendido} ({max_cantidad} unidades)")
print()

# 4. Analizar ventas por día (patrón de agrupación)
ventas_por_dia = {}

for venta in ventas:
    fecha = venta["fecha"]
    importe = venta["cantidad"] * venta["precio"]
    
    if fecha in ventas_por_dia:
        ventas_por_dia[fecha]["importe"] += importe
        ventas_por_dia[fecha]["transacciones"] += 1
    else:
        ventas_por_dia[fecha] = {"importe": importe, "transacciones": 1}

print("📅 VENTAS POR DÍA:")
for fecha, datos in ventas_por_dia.items():
    print(f"   • {fecha}: ${datos['importe']:,.2f} ({datos['transacciones']} transacciones)")

print("🔚 Análisis finalizado")

¡Ahora tienes un arsenal de patrones comunes para resolver problemas frecuentes en programación! En el próximo capítulo, aprenderás a visualizar el flujo de tus programas con diagramas.


🧭 Navegación:

Capítulos de esta sección: