QuieroProgramar por Rodri Gonzalez
Python · Lección 19 de 27

Funciones avanzadas

Lambda, *args/**kwargs, funciones como objetos, closures, decoradores y generators. Estas herramientas convierten código funcional en código elegante y profesional. Son las que aparecen en entrevistas técnicas y en el código de pandas y scikit-learn.

100–120 min Prerrequisitos: 18 Funciones básicas
01

Concepto teórico

*args y **kwargs — parámetros flexibles

A veces no sabés de antemano cuántos argumentos va a recibir tu función. Python resuelve esto con dos mecanismos:

*args — captura cualquier cantidad de argumentos posicionales como una tupla:

def sumar_todo(*args):
    return sum(args)  # args es una tupla

sumar_todo(1, 2, 3)         # args = (1, 2, 3) → 6
sumar_todo(10, 20, 30, 40)  # args = (10, 20, 30, 40) → 100

**kwargs — captura cualquier cantidad de argumentos nombrados como un diccionario:

def crear_ficha(**kwargs):
    for k, v in kwargs.items():
        print(f"  {k}: {v}")

crear_ficha(nombre="García", score=720, ciudad="BsAs")
# kwargs = {"nombre": "García", "score": 720, "ciudad": "BsAs"}

Podés combinar ambos con parámetros normales. El orden obligatorio es: def f(normales, *args, **kwargs).

¿Dónde se usan en la práctica? Los ves constantemente en librerías: print(*args) acepta cualquier cantidad de valores. pd.read_csv(path, **kwargs) acepta docenas de parámetros opcionales. dict(**kwargs) crea diccionarios. Entender *args/**kwargs te permite leer el código fuente de pandas y scikit-learn.

Lambda — funciones anónimas

Una lambda es una función sin nombre, definida en una sola línea. Se usa cuando necesitás una función simple y descartable — típicamente como argumento de sorted(), map(), filter() o df.apply().

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

# Equivalente lambda
doble = lambda x: x * 2

# Uso típico: como key en sorted
sorted(clientes, key=lambda c: c["score"])
Regla de estilo PEP 8: nunca asignes una lambda a una variable (f = lambda x: x*2). Si necesitás reusar la función, usá def. Las lambdas son para uso inline y descartable (dentro de sorted, map, filter, apply).

Funciones como objetos (first-class functions)

En Python, las funciones son objetos de primera clase. Esto significa que podés: asignarlas a variables, pasarlas como argumentos a otras funciones, devoverlas desde funciones, y guardarlas en listas o diccionarios.

Esto habilita patrones muy poderosos como callbacks, estrategias dinámicas y pipelines configurables.

Closures — funciones que recuerdan su contexto

Un closure es una función interna que "recuerda" las variables del scope donde fue creada, incluso después de que ese scope termine. Es como una función con memoria:

def crear_calculadora_iva(tasa):
    def calcular(precio):
        return precio * (1 + tasa)  # "recuerda" tasa
    return calcular

iva_21 = crear_calculadora_iva(0.21)
iva_105 = crear_calculadora_iva(0.105)
print(iva_21(1000))   # 1210.0
print(iva_105(1000))  # 1105.0

Decoradores — modificar funciones sin tocarlas

Un decorador es una función que envuelve otra función para agregarle funcionalidad sin modificar su código. Usan la sintaxis @decorador arriba de la función. Son la base de frameworks como Flask y Django:

def medir_tiempo(func):
    def wrapper(*args, **kwargs):
        import time
        inicio = time.time()
        resultado = func(*args, **kwargs)
        print(f"{func.__name__} tardó {time.time()-inicio:.4f}s")
        return resultado
    return wrapper

@medir_tiempo
def procesar_datos(n):
    return sum(range(n))

procesar_datos(1000000)  # imprime el tiempo automáticamente

Generators — iteradores perezosos con yield

Un generator es una función que usa yield en vez de return. En vez de calcular todos los resultados de una vez y guardarlos en memoria, genera uno a la vez. Ideal para datasets grandes donde no querés cargar todo en RAM.

def generar_cuotas(capital, tasa, n):
    saldo = capital
    for mes in range(1, n+1):
        interes = saldo * tasa
        yield mes, interes, saldo  # devuelve y "pausa"
        saldo -= (capital / n)

for mes, interes, saldo in generar_cuotas(100000, 0.05, 6):
    print(f"Mes {mes}: interés ${interes:,.0f}")
En pandas/ML: pd.read_csv(chunksize=1000) devuelve un generator que lee 1000 filas por vez. sklearn usa generators internamente para datasets grandes. Lambda se usa en df.apply(lambda x: ...) constantemente. sorted(df, key=lambda x: x["col"]) es el patrón más común de lambda.
Tip profesional: no necesitás dominar todo esto el día uno. Las prioridades son: (1) lambda para sorted/map/apply — lo usás todos los días, (2) *args/**kwargs para leer código de librerías, (3) decoradores para entender frameworks, (4) generators cuando trabajes con datos muy grandes.
02

Ejemplos explicados paso a paso

Ejemplo 1: *args y **kwargs

ejemplo_01_args_kwargs.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 2: Lambda — funciones inline

ejemplo_02_lambda.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 3: Funciones como objetos — pasar funciones como argumentos

ejemplo_03_first_class.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 4: Closures y fábricas de funciones

ejemplo_04_closures.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 5: Decoradores y generators

ejemplo_05_decoradores.pyPython

        
Hacé clic en ▶ Ejecutar
03

Referencia rápida

Concepto Sintaxis Cuándo usar
*args def f(*args): Cantidad variable de args posicionales
**kwargs def f(**kwargs): Cantidad variable de args nombrados
Lambda lambda x: x*2 Inline en sorted/map/filter/apply
Func como arg f(otra_func) Callbacks, estrategias, pipelines
Closure def ext(): def int(): return int Fábricas de funciones, configuración
Decorador @deco def f(): Logging, timing, validación, caching
Generator def f(): yield x Datos grandes, iteración perezosa
Gen expression (x for x in iter) Comprehension perezosa (sin memoria)
Patrón Ejemplo práctico en análisis de datos
Lambda en sorted sorted(clientes, key=lambda c: c["score"])
Lambda en apply df["cat"] = df["score"].apply(lambda s: "Alto" if s>700 else "Bajo")
**kwargs en configs pd.read_csv("datos.csv", sep=";", encoding="latin-1")
Generator para chunks for chunk in pd.read_csv("big.csv", chunksize=10000):
04

Ejercicios

Nivel 1 · Básico

Ejercicio 1: Función con *args

Creá sumar(*numeros) que sume cualquier cantidad de números. Probá con 3 y con 5 argumentos. Debe incluir 150.

ejercicio_01.pyDebe incluir "150"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 2: Lambda en sorted

Ordená [("GGAL",5200),("YPF",28000),("PAMP",3100)] por precio descendente con lambda. Debe incluir YPF.

ejercicio_02.pyDebe incluir "YPF"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 3: **kwargs para crear registros

Creá crear_registro(**datos) que devuelva un diccionario. Probá creando un cliente. Debe incluir García.

ejercicio_03.pyDebe incluir "García"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 4: Lambda con map y filter

Dada montos = [1500, -200, 3200, -500, 4100], usá filter con lambda para quedarte solo con positivos, y map con lambda para aplicar IVA 21%. Debe incluir 10648 (suma con IVA).

ejercicio_04.pyDebe incluir "10648"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 5: Closure — fábrica de calculadoras

Creá crear_convertidor(tasa) que devuelva una función que multiplique por la tasa. Usá para crear a_dolares (÷1150) y a_pesos (×1150). Debe incluir USD.

ejercicio_05.pyDebe incluir "USD"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 6: Función como argumento

Creá procesar_lista(datos, func) que aplique func a cada elemento. Probá con str.upper y con una lambda. Debe incluir GARCÍA.

ejercicio_06.pyDebe incluir "GARCÍA"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 7: Generator para cuotas

Creá un generator cuotas(capital, n) que yield las cuotas de un préstamo (capital/n). Consumilo con for. Capital $120,000 en 6 cuotas. Debe incluir 20000.

ejercicio_07.pyDebe incluir "20000"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 8: Decorador simple

Creá un decorador @log que imprima "Llamando a [nombre]..." antes de ejecutar la función. Aplicalo a una función sumar(a,b). Debe incluir Llamando.

ejercicio_08.pyDebe incluir "Llamando"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 9: Dispatch con dict de funciones

Creá un dict operaciones donde las claves son strings ("sumar","restar","multiplicar") y los valores son lambdas. Usalo para ejecutar "multiplicar" con (15, 8). Debe incluir 120.

ejercicio_09.pyDebe incluir "120"

          
Hacé clic en ▶ Ejecutar
Nivel 4 · Desafío

Ejercicio 10: Pipeline configurable con funciones avanzadas

Creá un sistema de pipeline donde: (1) definís funciones de transformación (limpiar, convertir, clasificar), (2) las guardás en una lista pasos, (3) una función ejecutar_pipeline(dato, pasos) aplica cada paso en orden. Procesá " $1,500.00 ". Debe incluir pipeline.

ejercicio_10_desafio.pyDebe incluir "pipeline"

          
Hacé clic en ▶ Ejecutar
05

Resumen y conexión

En la siguiente lección (20 · Módulos de la stdlib) vas a explorar la biblioteca estándar de Python: math, datetime, random, json, os, collections — herramientas que vienen incluidas y que usás todos los días.

Recursos: Python docs — More on Functions · Functional Programming HOWTO