Lección 5 de 29Módulo 1: Fundamentos de Python

05. Funciones y Módulos

Crea funciones reutilizables y organiza tu código profesionalmente

25 minutos

Aprende a crear funciones reutilizables y organizar tu código profesionalmente con módulos. Las funciones son el corazón de la programación eficiente.

¿Por qué usar funciones?

Las funciones te permiten:

  • Reutilizar código: Escribe una vez, usa muchas veces
  • Organización: Código más limpio y mantenible
  • Testing: Fácil de probar y debuggear
  • Colaboración: Otros entienden tu código rápidamente

Crear funciones básicas

Sintaxis fundamental

def nombre_funcion():
    """Docstring: explica qué hace la función"""
    # Código de la función
    print("Hola desde la función")

# Llamar a la función
nombre_funcion()

Funciones con parámetros

def saludar(nombre):
    """Saluda a una persona por su nombre"""
    print(f"Hola, {nombre}!")

saludar("María")    # Hola, María!
saludar("Carlos")   # Hola, Carlos!

Funciones que retornan valores

def calcular_iva(precio, tasa_iva=0.21):
    """
    Calcula el IVA de un precio.

    Args:
        precio: Precio base sin IVA
        tasa_iva: Tasa de IVA (default 21%)

    Returns:
        Precio total con IVA incluido
    """
    iva = precio * tasa_iva
    total = precio + iva
    return total

precio_final = calcular_iva(100)
print(f"Precio con IVA: ${precio_final}")  # $121.0

# Usar tasa diferente
precio_final_reducido = calcular_iva(100, 0.10)
print(f"Precio con IVA reducido: ${precio_final_reducido}")  # $110.0

Funciones para análisis de datos

Calcular métricas de campaña

def calcular_metricas_campaña(impresiones, clics, conversiones, costo):
    """
    Calcula métricas clave de una campaña digital.

    Returns:
        dict: Diccionario con todas las métricas calculadas
    """
    ctr = (clics / impresiones) * 100 if impresiones > 0 else 0
    conversion_rate = (conversiones / clics) * 100 if clics > 0 else 0
    cpc = costo / clics if clics > 0 else 0
    cpa = costo / conversiones if conversiones > 0 else 0

    return {
        "ctr": round(ctr, 2),
        "conversion_rate": round(conversion_rate, 2),
        "cpc": round(cpc, 2),
        "cpa": round(cpa, 2)
    }

# Usar la función
metricas = calcular_metricas_campaña(
    impresiones=100000,
    clics=3000,
    conversiones=150,
    costo=1500
)

print("Métricas de campaña:")
print(f"  CTR: {metricas['ctr']}%")
print(f"  Conversion Rate: {metricas['conversion_rate']}%")
print(f"  CPC: ${metricas['cpc']}")
print(f"  CPA: ${metricas['cpa']}")

Resultado:

Métricas de campaña:
  CTR: 3.0%
  Conversion Rate: 5.0%
  CPC: $0.5
  CPA: $10.0

Análisis de ventas

def analizar_ventas(ventas):
    """
    Analiza una lista de ventas y retorna estadísticas.

    Args:
        ventas: Lista de valores de ventas

    Returns:
        dict: Estadísticas calculadas
    """
    if not ventas:
        return None

    total = sum(ventas)
    promedio = total / len(ventas)
    maximo = max(ventas)
    minimo = min(ventas)
    rango = maximo - minimo

    # Calcular mediana
    ventas_ordenadas = sorted(ventas)
    n = len(ventas_ordenadas)
    if n % 2 == 0:
        mediana = (ventas_ordenadas[n//2 - 1] + ventas_ordenadas[n//2]) / 2
    else:
        mediana = ventas_ordenadas[n//2]

    return {
        "total": total,
        "promedio": promedio,
        "mediana": mediana,
        "maximo": maximo,
        "minimo": minimo,
        "rango": rango,
        "cantidad": len(ventas)
    }

# Analizar ventas mensuales
ventas_mensuales = [45000, 52000, 48000, 61000, 55000, 70000]
stats = analizar_ventas(ventas_mensuales)

print("Estadísticas de ventas:")
print(f"  Total: ${stats['total']:,}")
print(f"  Promedio: ${stats['promedio']:,.2f}")
print(f"  Mediana: ${stats['mediana']:,.2f}")
print(f"  Máximo: ${stats['maximo']:,}")
print(f"  Mínimo: ${stats['minimo']:,}")
print(f"  Rango: ${stats['rango']:,}")

Parámetros avanzados

Parámetros con valores por defecto

def generar_reporte(titulo, datos, formato="pdf", incluir_graficos=True):
    """Genera un reporte con opciones configurables"""
    print(f"Generando reporte: {titulo}")
    print(f"  Formato: {formato}")
    print(f"  Incluir gráficos: {incluir_graficos}")
    print(f"  Registros: {len(datos)}")

# Usar defaults
generar_reporte("Ventas Q1", [1,2,3])

# Sobrescribir algunos defaults
generar_reporte("Ventas Q1", [1,2,3], formato="excel")

# Usar argumentos nombrados (keywords)
generar_reporte("Ventas Q1", [1,2,3], incluir_graficos=False, formato="html")

*args: Número variable de argumentos

def calcular_promedio(*numeros):
    """Calcula el promedio de cualquier cantidad de números"""
    if not numeros:
        return 0
    return sum(numeros) / len(numeros)

print(calcular_promedio(10, 20, 30))              # 20.0
print(calcular_promedio(100, 200, 300, 400))      # 250.0
print(calcular_promedio(5))                        # 5.0

**kwargs: Argumentos nombrados variables

def crear_usuario(**datos):
    """Crea un usuario con cualquier cantidad de atributos"""
    print("Creando usuario con los siguientes datos:")
    for clave, valor in datos.items():
        print(f"  {clave}: {valor}")

crear_usuario(
    nombre="María",
    edad=28,
    email="maria@empresa.com",
    ciudad="Madrid",
    rol="Analista"
)

Combinación de tipos de parámetros

def registrar_venta(producto_id, cantidad, precio, *extras, **metadata):
    """
    Registra una venta con datos flexibles.

    Args:
        producto_id: ID del producto (posicional)
        cantidad: Cantidad vendida (posicional)
        precio: Precio unitario (posicional)
        *extras: Datos adicionales como lista
        **metadata: Datos adicionales como dict
    """
    total = cantidad * precio

    print(f"Venta registrada:")
    print(f"  Producto: {producto_id}")
    print(f"  Cantidad: {cantidad}")
    print(f"  Precio unitario: ${precio}")
    print(f"  Total: ${total}")

    if extras:
        print(f"  Extras: {extras}")

    if metadata:
        print(f"  Metadata:")
        for key, value in metadata.items():
            print(f"    {key}: {value}")

registrar_venta(
    "LAPTOP-001",
    2,
    800,
    "Con garantía extendida",
    "Envío express",
    vendedor="Carlos",
    sucursal="Madrid Centro",
    promocion="Black Friday"
)

Lambda functions (funciones anónimas)

# Función normal
def cuadrado(x):
    return x ** 2

# Lambda equivalente (función anónima)
cuadrado_lambda = lambda x: x ** 2

print(cuadrado(5))         # 25
print(cuadrado_lambda(5))  # 25

# Lambdas son útiles con funciones de orden superior
ventas = [1200, 1450, 1100, 1800, 1950]

# Filtrar ventas > 1400
ventas_altas = list(filter(lambda x: x > 1400, ventas))
print(ventas_altas)  # [1450, 1800, 1950]

# Aplicar descuento del 10%
ventas_con_descuento = list(map(lambda x: x * 0.9, ventas))
print(ventas_con_descuento)  # [1080.0, 1305.0, 990.0, 1620.0, 1755.0]

# Ordenar productos por precio
productos = [
    {"nombre": "Laptop", "precio": 800},
    {"nombre": "Mouse", "precio": 25},
    {"nombre": "Teclado", "precio": 60}
]

productos_ordenados = sorted(productos, key=lambda p: p["precio"])
for p in productos_ordenados:
    print(f"{p['nombre']}: ${p['precio']}")

Scope de variables

# Variable global
empresa = "Tooldata"

def funcion_con_variables():
    # Variable local
    empleados = 50
    print(f"Empresa: {empresa}")      # Accede a global
    print(f"Empleados: {empleados}")  # Accede a local

funcion_con_variables()
print(f"Empresa: {empresa}")          # OK
# print(f"Empleados: {empleados}")    # Error: empleados no existe aquí

# Modificar variable global (no recomendado)
contador = 0

def incrementar_contador():
    global contador  # Declarar que usamos la variable global
    contador += 1

incrementar_contador()
print(contador)  # 1

Módulos: Organizar código en archivos

Crear tu propio módulo

Crea un archivo analytics_tools.py:

# analytics_tools.py
"""
Módulo de herramientas para análisis de datos.
"""

def calcular_ctr(impresiones, clics):
    """Calcula el CTR (Click-Through Rate)"""
    if impresiones == 0:
        return 0
    return (clics / impresiones) * 100

def calcular_roi(ingresos, inversion):
    """Calcula el ROI (Return on Investment)"""
    if inversion == 0:
        return 0
    return ((ingresos - inversion) / inversion) * 100

def clasificar_cliente(valor_compras):
    """Clasifica clientes según valor de compras"""
    if valor_compras >= 10000:
        return "VIP"
    elif valor_compras >= 5000:
        return "Premium"
    elif valor_compras >= 1000:
        return "Regular"
    else:
        return "Nuevo"

# Constantes del módulo
IVA_ESTANDAR = 0.21
IVA_REDUCIDO = 0.10

Usar tu módulo

# En tu notebook o script principal
import analytics_tools

# Usar funciones del módulo
ctr = analytics_tools.calcular_ctr(100000, 3000)
print(f"CTR: {ctr}%")

roi = analytics_tools.calcular_roi(50000, 10000)
print(f"ROI: {roi}%")

categoria = analytics_tools.clasificar_cliente(7500)
print(f"Categoría: {categoria}")

# Acceder a constantes
precio = 100
precio_con_iva = precio * (1 + analytics_tools.IVA_ESTANDAR)
print(f"Precio con IVA: ${precio_con_iva}")

Diferentes formas de importar

# Importar todo el módulo
import analytics_tools
resultado = analytics_tools.calcular_ctr(1000, 30)

# Importar funciones específicas
from analytics_tools import calcular_ctr, calcular_roi
resultado = calcular_ctr(1000, 30)  # No necesitas prefijo

# Importar todo (no recomendado)
from analytics_tools import *
resultado = calcular_ctr(1000, 30)

# Importar con alias
import analytics_tools as at
resultado = at.calcular_ctr(1000, 30)

# Importar función con alias
from analytics_tools import calcular_ctr as ctr
resultado = ctr(1000, 30)

Módulos incorporados útiles

math: Operaciones matemáticas

import math

# Funciones matemáticas
print(math.sqrt(16))        # 4.0 (raíz cuadrada)
print(math.ceil(4.3))       # 5 (redondear arriba)
print(math.floor(4.9))      # 4 (redondear abajo)
print(math.log(100, 10))    # 2.0 (logaritmo)
print(math.pi)              # 3.141592...

# Cálculo de crecimiento exponencial
capital_inicial = 10000
tasa = 0.07
años = 5
capital_final = capital_inicial * math.exp(tasa * años)
print(f"Capital final: ${capital_final:,.2f}")

random: Números aleatorios

import random

# Número aleatorio entre 0 y 1
print(random.random())

# Número aleatorio en un rango
print(random.randint(1, 100))

# Elegir elemento aleatorio
productos = ["Laptop", "Mouse", "Teclado", "Monitor"]
print(random.choice(productos))

# Mezclar lista
random.shuffle(productos)
print(productos)

# Simular datos de prueba
ventas_simuladas = [random.randint(1000, 2000) for _ in range(7)]
print(f"Ventas simuladas: {ventas_simuladas}")

datetime: Manejo de fechas

from datetime import datetime, timedelta

# Fecha y hora actual
ahora = datetime.now()
print(f"Ahora: {ahora}")
print(f"Fecha: {ahora.date()}")
print(f"Hora: {ahora.time()}")

# Formatear fechas
print(ahora.strftime("%Y-%m-%d"))          # 2024-10-26
print(ahora.strftime("%d/%m/%Y %H:%M"))    # 26/10/2024 15:30

# Operaciones con fechas
ayer = ahora - timedelta(days=1)
proxima_semana = ahora + timedelta(weeks=1)
print(f"Ayer: {ayer.date()}")
print(f"Próxima semana: {proxima_semana.date()}")

# Calcular diferencia de días
fecha_inicio = datetime(2024, 1, 1)
dias_transcurridos = (ahora - fecha_inicio).days
print(f"Días transcurridos este año: {dias_transcurridos}")

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

# ventas_analyzer.py
"""Sistema completo de análisis de ventas"""

from datetime import datetime

def cargar_ventas():
    """Retorna datos de ejemplo de ventas"""
    return [
        {"fecha": "2024-01-15", "producto": "Laptop", "cantidad": 2, "precio": 800},
        {"fecha": "2024-01-16", "producto": "Mouse", "cantidad": 5, "precio": 25},
        {"fecha": "2024-01-16", "producto": "Teclado", "cantidad": 3, "precio": 60},
        {"fecha": "2024-01-17", "producto": "Monitor", "cantidad": 1, "precio": 300},
        {"fecha": "2024-01-18", "producto": "Laptop", "cantidad": 1, "precio": 800},
    ]

def calcular_total_venta(venta):
    """Calcula el total de una venta"""
    return venta["cantidad"] * venta["precio"]

def analizar_por_producto(ventas):
    """Agrupa ventas por producto"""
    productos = {}

    for venta in ventas:
        producto = venta["producto"]
        total_venta = calcular_total_venta(venta)

        if producto not in productos:
            productos[producto] = {
                "cantidad_total": 0,
                "ingresos_total": 0,
                "num_transacciones": 0
            }

        productos[producto]["cantidad_total"] += venta["cantidad"]
        productos[producto]["ingresos_total"] += total_venta
        productos[producto]["num_transacciones"] += 1

    return productos

def generar_reporte(ventas):
    """Genera reporte completo de ventas"""
    print("="*60)
    print("REPORTE DE VENTAS")
    print("="*60)

    # Análisis general
    total_ingresos = sum(calcular_total_venta(v) for v in ventas)
    total_transacciones = len(ventas)

    print(f"\nRESUMEN GENERAL:")
    print(f"  Total de transacciones: {total_transacciones}")
    print(f"  Ingresos totales: ${total_ingresos:,}")
    print(f"  Ticket promedio: ${total_ingresos / total_transacciones:,.2f}")

    # Análisis por producto
    print(f"\nANÁLISIS POR PRODUCTO:")
    productos = analizar_por_producto(ventas)

    for producto, stats in productos.items():
        print(f"\n  {producto}:")
        print(f"    Unidades vendidas: {stats['cantidad_total']}")
        print(f"    Ingresos: ${stats['ingresos_total']:,}")
        print(f"    Transacciones: {stats['num_transacciones']}")
        promedio = stats['ingresos_total'] / stats['num_transacciones']
        print(f"    Venta promedio: ${promedio:,.2f}")

    # Top producto
    top_producto = max(productos.items(), key=lambda x: x[1]['ingresos_total'])
    print(f"\n🏆 TOP PRODUCTO:")
    print(f"  {top_producto[0]} - ${top_producto[1]['ingresos_total']:,} en ingresos")

# Ejecutar análisis
if __name__ == "__main__":
    ventas = cargar_ventas()
    generar_reporte(ventas)

Buenas prácticas

1. Docstrings descriptivos

def calcular_descuento(precio, porcentaje, cliente_vip=False):
    """
    Calcula el precio final con descuento aplicado.

    Args:
        precio (float): Precio original del producto
        porcentaje (float): Porcentaje de descuento (0-100)
        cliente_vip (bool): Si es cliente VIP (descuento adicional 5%)

    Returns:
        float: Precio final con descuento aplicado

    Examples:
        >>> calcular_descuento(100, 10)
        90.0
        >>> calcular_descuento(100, 10, cliente_vip=True)
        85.5
    """
    descuento = precio * (porcentaje / 100)
    precio_con_descuento = precio - descuento

    if cliente_vip:
        descuento_vip = precio_con_descuento * 0.05
        precio_con_descuento -= descuento_vip

    return precio_con_descuento

2. Funciones pequeñas y enfocadas

Mal: Función que hace demasiado

def procesar_todo(datos):
    # 200 líneas de código haciendo muchas cosas...
    pass

Bien: Funciones específicas

def limpiar_datos(datos):
    # Solo limpia datos
    pass

def calcular_metricas(datos):
    # Solo calcula métricas
    pass

def generar_reporte(metricas):
    # Solo genera reporte
    pass

3. Validar inputs

def calcular_ctr(impresiones, clics):
    """Calcula CTR con validación"""
    # Validar tipos
    if not isinstance(impresiones, (int, float)) or not isinstance(clics, (int, float)):
        raise TypeError("Impresiones y clics deben ser números")

    # Validar valores
    if impresiones < 0 or clics < 0:
        raise ValueError("Los valores no pueden ser negativos")

    if clics > impresiones:
        raise ValueError("Los clics no pueden superar las impresiones")

    if impresiones == 0:
        return 0

    return (clics / impresiones) * 100

Resumen de la lección

✅ Las funciones permiten reutilizar código y mantenerlo organizado ✅ Usa parámetros para hacer funciones flexibles y return para devolver valores ✅ Docstrings documentan qué hace cada función ✅ Lambda functions son útiles para operaciones simples de una línea ✅ Los módulos organizan funciones relacionadas en archivos separados ✅ Python tiene módulos incorporados poderosos (math, random, datetime)


🎯 Próxima lección: 06. Introducción a Pandas

¡Felicidades! Completaste el Módulo 1. En el siguiente módulo comenzarás con Pandas, la librería más importante para análisis de datos. 🚀

¿Completaste esta lección?

Marca esta lección como completada. Tu progreso se guardará en tu navegador.