lección 6
Paradigmas: OOP y funcional en Python
Descubre los tres estilos de programar (imperativo, orientado a objetos, funcional) y por que importa entenderlos antes de usar Pandas y PySpark.
⏱ 60 min
### El problema: hay mas de una forma de organizar tu codigo
Hasta ahora has escrito codigo de una forma muy natural: defines variables, usas if/else para decidir, recorres datos con for y llamas a funciones. Este estilo se llama programacion imperativa — le dices al ordenador PASO A PASO que hacer, como si le dieras instrucciones a un cocinero novato: "coge el cuchillo, pela la patata, cortala en cubos, echala a la olla". Funciona perfectamente. Es claro y directo.
Pero resulta que existen otras dos formas de organizar el codigo que vas a necesitar entender ANTES de avanzar a herramientas mas potentes. Cuando uses Pandas y hagas df.groupby("categoria").sum(), estaras usando programacion orientada a objetos (OOP). Cuando uses PySpark y hagas df.filter(col("precio") > 100).select("producto"), estaras usando programacion funcional. Si no sabes que es cada cosa, esas lineas seran magia incomprensible. Si lo sabes, seran la consecuencia logica de lo que ya conoces.
Este capitulo no pretende convertirte en un experto en OOP ni en funcional — para eso hay cursos enteros. Lo que vamos a hacer es darte el MAPA MENTAL para que reconozcas estos patrones cuando los veas, entiendas por que existen y puedas usarlos con confianza. Es la diferencia entre conducir sabiendo como funciona un motor (puedes diagnosticar problemas) y conducir sin saber nada (todo es magia hasta que se rompe).
### Los tres paradigmas: imperativo, OOP y funcional
Un paradigma de programacion es una FORMA DE PENSAR sobre como resolver problemas con codigo. No es una tecnologia ni una libreria — es una filosofia. Y Python es especial porque soporta los tres paradigmas principales al mismo tiempo. Puedes mezclarlos segun lo que necesites. Eso lo hace increiblemente flexible, pero tambien puede confundir si nadie te explica que estas viendo tres estilos diferentes en el mismo lenguaje.
- IMPERATIVO: le dices al ordenador COMO hacer algo, paso a paso. "Recorre esta lista, si el precio es mayor a 100, anadelo a otra lista, al final devuelve esa lista." Es lo que has hecho hasta ahora.
- ORIENTADO A OBJETOS (OOP): organizas tu codigo alrededor de OBJETOS que tienen datos (atributos) y comportamientos (metodos). "El objeto lista SABE ordenarse. El objeto string SABE convertirse a mayusculas."
- FUNCIONAL: construyes tu programa COMBINANDO funciones que transforman datos. "Filtra esto, luego transforma aquello, luego reduce a un resultado." Sin modificar nada — cada paso produce algo nuevo.
La analogia del restaurante: el estilo imperativo es como darle instrucciones paso a paso a un cocinero novato — "pon la sarten, echa aceite, espera a que caliente, pon la carne". El estilo OOP es como tener utensilios inteligentes — "sarten.calentar()" y la sarten ya sabe que hacer por dentro. El estilo funcional es como una cadena de montaje — cada estacion transforma el plato y lo pasa a la siguiente, sin que nadie vuelva atras a modificar lo que ya paso.
### OOP en Python: ya lo estas usando sin saberlo
Aqui viene la revelacion que cambia como ves Python: TODO en Python es un objeto. Cuando escribes "hola".upper(), estas llamando al METODO upper() del OBJETO string "hola". Cuando haces lista.append(x), estas pidiendo al OBJETO lista que ejecute su METODO append. Cuando usas len(datos), Python internamente llama al metodo __len__() del objeto datos. Has estado usando OOP desde la primera leccion sin darte cuenta.
La idea fundamental de OOP es esta: en vez de tener DATOS por un lado y FUNCIONES por otro, los empaquetas juntos en un OBJETO. El objeto string "hola" CONTIENE los caracteres y SABE como transformarse (upper, lower, split, strip...). El objeto lista [1,2,3] CONTIENE los elementos y SABE como gestionarse (append, sort, pop...). Los datos y las operaciones sobre esos datos viajan juntos.
1# Todo en Python es un objeto con metodos2texto = " datos de ventas del lunes "34# El objeto string SABE transformarse5print(texto.strip()) # quita espacios6print(texto.upper()) # mayusculas7print(texto.split()) # divide en palabras8print(texto.replace("lunes", "martes")) # sustituye910# El objeto lista SABE gestionarse11ventas = [120.5, 89.0, 245.0, 67.3]12ventas.append(180.0) # se anade a si misma13ventas.sort() # se ordena a si misma14print(ventas) # [67.3, 89.0, 120.5, 180.0, 245.0]1516# El objeto diccionario SABE consultarse17cliente = {"nombre": "Ana", "plan": "premium"}18print(cliente.keys()) # sabe sus claves19print(cliente.get("edad", 0)) # sabe buscar de forma segura20cliente.update({"edad": 28}) # sabe actualizarse
Cada vez que usas un punto (.) despues de un valor, estas usando OOP.
Por que esto importa para tu futuro? Porque TODAS las librerias de datos usan OOP. Cuando escribas df.groupby("cat").sum() en Pandas, df es un objeto DataFrame que tiene un metodo groupby() que devuelve OTRO objeto sobre el cual llamas sum(). Es una CADENA de metodos — cada uno devuelve un objeto nuevo con mas metodos disponibles. Entender que cada punto es "pedirle algo a un objeto" te desbloquea Pandas, PySpark y cualquier libreria Python.
Consejo de senior: no necesitas crear tus propias clases para usar OOP en datos. Lo que necesitas es entender que cuando ves algo.metodo().otro_metodo(), son objetos que devuelven objetos. Eso es el 95% de lo que necesitas saber de OOP para ingenieria de datos. Crear clases propias es util, pero es un tema para mas adelante.
### Metodos encadenados: el patron que domina Pandas y PySpark
El patron mas potente de OOP para datos es el ENCADENAMIENTO de metodos (method chaining). La idea: cada metodo devuelve un objeto, y sobre ese objeto puedes llamar a otro metodo. Es como una cadena de montaje donde cada estacion transforma el producto y lo pasa a la siguiente.
1# Encadenamiento de metodos con strings2nombre_sucio = " ana GARCIA lopez "34# Sin encadenar (verboso)5paso1 = nombre_sucio.strip()6paso2 = paso1.lower()7paso3 = paso2.title()8print(paso3) # "Ana Garcia Lopez"910# Encadenado (una linea — cada metodo devuelve un string nuevo)11nombre_limpio = nombre_sucio.strip().lower().title()12print(nombre_limpio) # "Ana Garcia Lopez"1314# Asi se ve en Pandas (futuro):15# df.dropna().groupby("categoria")["importe"].sum().sort_values(ascending=False)16# | | | |17# quita NaN agrupa suma ordena18#19# Cada punto produce un objeto nuevo con mas metodos disponibles
Cada metodo devuelve un objeto. Sobre ese objeto llamas al siguiente. Es una cadena.
### Programacion funcional: transformar sin mutar
El tercer paradigma es la programacion funcional. Su filosofia es radical: NUNCA modificar datos existentes. En vez de cambiar una lista, creas una NUEVA lista con los cambios. En vez de modificar un diccionario, creas uno NUEVO. Cada transformacion produce algo nuevo sin tocar el original.
Por que alguien querria eso? Piensa en un pipeline de datos con 5 pasos. Si el paso 3 modifica los datos del paso 1, y luego el paso 5 necesita los datos originales... tienes un problema. La programacion funcional evita esa clase de bugs: como nada se modifica, puedes encadenar transformaciones sin miedo a efectos secundarios. PySpark usa este principio al 100% — cada operacion produce un DataFrame nuevo.
La buena noticia: ya has usado programacion funcional sin saberlo. Las list comprehensions SON programacion funcional — crean una lista nueva a partir de otra sin modificar la original. sorted() ES funcional — devuelve una lista nueva en vez de modificar la existente. La diferencia con .sort() (que modifica la lista original) es exactamente la diferencia entre estilo funcional y estilo imperativo.
1# IMPERATIVO (modifica los datos originales)2precios = [100, 250, 50, 899, 1300]3precios.sort() # modifica la lista original!4print(precios) # [50, 100, 250, 899, 1300]56# FUNCIONAL (crea datos nuevos, el original no se toca)7precios = [100, 250, 50, 899, 1300]8ordenados = sorted(precios) # crea una lista NUEVA9print(precios) # [100, 250, 50, 899, 1300] — intacto!10print(ordenados) # [50, 100, 250, 899, 1300] — copia ordenada1112# List comprehension = programacion funcional que ya conoces13precios = [100, 250, 50, 899, 1300]14con_iva = [p * 1.21 for p in precios] # lista NUEVA15caros = [p for p in precios if p > 200] # otra lista NUEVA1617print(precios) # sigue siendo [100, 250, 50, 899, 1300]18print(con_iva) # [121.0, 302.5, 60.5, 1087.79, 1573.0]19print(caros) # [250, 899, 1300]
Funcional = transformar SIN modificar el original. Cada operacion produce algo nuevo.
### map, filter y lambda: las herramientas funcionales puras
Python tiene tres funciones built-in que representan la esencia de la programacion funcional: map() transforma cada elemento, filter() selecciona elementos por condicion, y lambda crea funciones anonimas de una linea. Las list comprehensions que ya conoces son la version "pytonica" de map y filter — pero entender las versiones puras te prepara para PySpark, donde se usan constantemente.
1# lambda: funcion anonima de una linea2def aplicar_iva(precio):3 return precio * 1.2145# Lo mismo como lambda:6aplicar_iva_fn = lambda precio: precio * 1.217print(aplicar_iva_fn(100)) # 121.089# map(): aplica una funcion a CADA elemento10precios = [100, 250, 50, 899]11con_iva = list(map(lambda p: p * 1.21, precios))12print(con_iva) # [121.0, 302.5, 60.5, 1087.79]1314# Equivalente con list comprehension (mas pythonico):15con_iva = [p * 1.21 for p in precios]1617# filter(): selecciona elementos que cumplen una condicion18caros = list(filter(lambda p: p > 200, precios))19print(caros) # [250, 899]2021# Equivalente con list comprehension:22caros = [p for p in precios if p > 200]2324# Encadenar map + filter (estilo funcional puro):25resultado = list(map(26 lambda p: round(p * 1.21, 2),27 filter(lambda p: p > 200, precios)28))29print(resultado) # [302.5, 1087.79]
map = transformar cada elemento. filter = seleccionar por condicion. lambda = funcion de una linea.
En Python idiomatico, las list comprehensions se prefieren sobre map/filter para la mayoria de casos — son mas legibles. Pero map/filter aparecen constantemente en documentacion de PySpark, APIs funcionales y codigo legacy. Entenderlos te permite leer ESE codigo sin bloquearte.
### Por que importa: Pandas usa OOP, PySpark usa funcional
Ahora viene el punto clave de toda esta leccion. Las herramientas que vas a usar el resto del curso tienen una PREFERENCIA de paradigma. Pandas prefiere el estilo OOP: creas un objeto DataFrame y llamas metodos sobre el (.groupby(), .merge(), .fillna()). PySpark prefiere el estilo funcional: encadenas transformaciones que producen DataFrames nuevos sin modificar el original (.filter(), .select(), .withColumn()). Entender esta diferencia te ahorrara confusiones enormes.
1# Lo que tu ya sabes hacer HOY (Python puro)2ventas = [3 {"producto": "Laptop", "categoria": "tech", "importe": 1299},4 {"producto": "Camiseta", "categoria": "ropa", "importe": 25},5 {"producto": "Monitor", "categoria": "tech", "importe": 449},6 {"producto": "Pantalon", "categoria": "ropa", "importe": 60},7]89# Estilo funcional (filter + transform con comprehension):10tech_grandes = [11 v["producto"]12 for v in ventas13 if v["categoria"] == "tech" and v["importe"] > 20014]15print(f"Productos tech caros: {tech_grandes}") # ['Laptop', 'Monitor']1617# En Pandas (futuro) seria:18# df[df["categoria"] == "tech"][df["importe"] > 200]["producto"]19#20# En PySpark (futuro) seria:21# df.filter((col("categoria") == "tech") & (col("importe") > 200)).select("producto")
Tu ya sabes la logica base. Pandas y PySpark son la misma logica con sintaxis diferente.
### sorted() con key: funciones como argumentos
Un concepto funcional que ya has visto: pasar una FUNCION como argumento a otra funcion. Cuando haces sorted(lista, key=len), estas pasando la funcion len como "criterio de ordenacion". No ejecutas len() — pasas LA FUNCION MISMA para que sorted() la use internamente. Este patron es la base de filter(), map(), y de como PySpark aplica transformaciones.
1# Funciones como argumentos (funciones de orden superior)2productos = ["laptop", "a", "monitor 4K", "teclado mecanico"]34# Ordenar por longitud del nombre5por_longitud = sorted(productos, key=len)6print(por_longitud) # ['a', 'laptop', 'monitor 4K', 'teclado mecanico']78# Ordenar registros por un campo9ventas = [10 {"producto": "Laptop", "importe": 1299},11 {"producto": "Raton", "importe": 30},12 {"producto": "Monitor", "importe": 449},13]1415por_importe = sorted(ventas, key=lambda v: v["importe"], reverse=True)16for v in por_importe:17 print(f" {v['producto']}: {v['importe']}e")1819# max/min tambien aceptan key:20mas_caro = max(ventas, key=lambda v: v["importe"])21print(f"Mas caro: {mas_caro['producto']}") # Laptop
Pasar funciones como argumentos (key=) es programacion funcional en accion.
Lo que le diria a mi yo de hace 5 anios: no te obsesiones con crear clases ni con dominar programacion funcional "pura". Lo que necesitas es RECONOCER los patrones. Cuando veas objeto.metodo(), sabes que es OOP. Cuando veas funcion(lambda: ..., datos), sabes que es funcional. Cuando veas un for paso a paso, sabes que es imperativo. Reconocer el patron es el 80% del valor.
### Resumen: cuando usar cada paradigma
- IMPERATIVO (for, if, variables): scripts simples, logica de negocio clara paso a paso, debugging. Cuando quieres que cualquiera entienda el flujo.
- OOP (objeto.metodo()): usar librerias como Pandas, manipular DataFrames, encadenar transformaciones legibles. No necesitas crear clases — solo USAR las que otros crearon.
- FUNCIONAL (map, filter, lambda, comprehensions): transformaciones de datos sin efectos secundarios. PySpark, procesamiento de streams, situaciones donde no quieres modificar los datos originales.
En la practica diaria de ingenieria de datos, mezclaras los tres estilos en el mismo script sin pensarlo: un for imperativo para recorrer archivos, una llamada OOP para leer con Pandas (pd.read_csv), y un filter funcional para limpiar valores nulos. Python te permite esa flexibilidad. Lo importante es que ahora SABES lo que estas haciendo en cada momento.
## ejercicios
Identifica el paradigma
Clasifica varios fragmentos de codigo como imperativo, OOP o funcional segun el patron que usan.
El mismo problema, tres estilos
Filtra ventas mayores a 100 euros y calcula el total con IVA. Hazlo de tres formas: imperativa (for), funcional (map/filter) y con list comprehension.
Limpiar datos con metodos encadenados
Tienes nombres sucios (espacios, mayusculas inconsistentes). Limpia cada uno usando encadenamiento de metodos de string.
Mini pipeline funcional
Usa SOLO estilo funcional (map, filter, reduce) para calcular el gasto total con IVA de clientes VIP (gasto > 500).
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...