Python
Tutorial Python: Paradigma funcional
Aprende el paradigma funcional en Python con funciones puras, inmutabilidad y funciones como objetos para código predecible y modular.
Aprende Python y certifícateFunciones puras
En el corazón del paradigma funcional encontramos las funciones puras, uno de los conceptos fundamentales que distingue este enfoque de la programación imperativa tradicional. Una función pura es aquella que cumple dos características esenciales:
- Dado el mismo input, siempre produce el mismo output
- No tiene efectos secundarios (no modifica estados fuera de su ámbito)
Estas propiedades hacen que las funciones puras sean predecibles, fáciles de probar y razonar sobre ellas. Veamos cómo se aplica este concepto en Python.
Identificando funciones puras
Para entender mejor qué es una función pura, comparemos ejemplos de funciones puras e impuras:
# Función pura
def sumar(a, b):
return a + b
# Función pura
def duplicar_elementos(lista):
return [x * 2 for x in lista]
Estas funciones son puras porque:
- Siempre devuelven el mismo resultado para los mismos argumentos
- No modifican variables externas ni los argumentos recibidos
- No realizan operaciones de E/S (entrada/salida)
En contraste, veamos algunas funciones impuras:
total = 0
# Función impura: modifica una variable global
def agregar_al_total(valor):
global total
total += valor
return total
# Función impura: tiene efectos secundarios (E/S)
def guardar_resultado(resultado):
with open("resultado.txt", "w") as f:
f.write(str(resultado))
return True
# Función impura: depende del estado externo
def obtener_temperatura():
# Imaginemos que esto consulta un servicio web
return requests.get("https://api.clima.com/temperatura").json()["valor"]
Beneficios de las funciones puras
Las funciones puras ofrecen varias ventajas:
- Facilidad de prueba: Al no depender de estados externos, son más fáciles de probar de forma aislada.
- Predictibilidad: Siempre producen el mismo resultado para las mismas entradas.
- Transparencia referencial: Pueden ser reemplazadas por su valor de retorno sin cambiar el comportamiento del programa.
- Paralelización: Al no compartir estado, pueden ejecutarse en paralelo sin problemas de concurrencia.
Aplicando funciones puras en código real
Veamos cómo podemos refactorizar código impuro para hacerlo más funcional:
# Enfoque imperativo (impuro)
def calcular_precios_con_impuesto(productos):
for i in range(len(productos)):
productos[i]['precio'] = productos[i]['precio'] * 1.21
return productos
# Enfoque funcional (puro)
def calcular_precios_con_impuesto_puro(productos):
return [
{**producto, 'precio': producto['precio'] * 1.21}
for producto in productos
]
En el primer caso, estamos modificando la lista original, lo que constituye un efecto secundario. En el segundo caso, creamos una nueva lista sin modificar la original, manteniendo la pureza de la función.
Composición de funciones puras
Una de las ventajas de las funciones puras es que se pueden componer fácilmente para crear funciones más complejas:
def filtrar_mayores_edad(personas):
return [p for p in personas if p['edad'] >= 18]
def ordenar_por_nombre(personas):
return sorted(personas, key=lambda p: p['nombre'])
def obtener_adultos_ordenados(personas):
return ordenar_por_nombre(filtrar_mayores_edad(personas))
Cada función realiza una tarea específica y se pueden combinar para lograr comportamientos más complejos.
Funciones puras con argumentos por defecto
Las funciones puras pueden tener argumentos por defecto, pero hay que tener cuidado con los objetos mutables:
# ¡Cuidado! Esta función no es pura debido al argumento mutable por defecto
def agregar_item_impura(item, lista=[]):
lista.append(item)
return lista
# Versión pura con argumento por defecto
def agregar_item_pura(item, lista=None):
if lista is None:
lista = []
return lista + [item] # Crea una nueva lista en lugar de modificar la original
Funciones puras con decoradores
Podemos usar decoradores para transformar funciones impuras en puras o para añadir funcionalidad sin comprometer la pureza:
import functools
def memoize(func):
"""Decorador que cachea los resultados de una función pura."""
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
"""Función pura que calcula el n-ésimo número de Fibonacci."""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
Este decorador memoize
aprovecha la propiedad de las funciones puras (mismo input, mismo output) para cachear resultados y mejorar el rendimiento sin afectar la pureza de la función.
Manejo de errores en funciones puras
Las funciones puras también deben manejar los errores de forma predecible:
def dividir_seguro(a, b):
"""Función pura que maneja el caso de división por cero."""
if b == 0:
return None # O podríamos usar Optional[float] con tipado
return a / b
# Alternativa usando excepciones (sigue siendo pura)
def raiz_cuadrada(x):
"""Función pura que calcula la raíz cuadrada si es posible."""
if x < 0:
raise ValueError("No se puede calcular la raíz cuadrada de un número negativo")
return x ** 0.5
Funciones puras en el procesamiento de datos
Las funciones puras son especialmente útiles en el procesamiento de datos:
def normalizar_datos(datos):
"""Normaliza una lista de números al rango [0,1]."""
if not datos:
return []
minimo = min(datos)
maximo = max(datos)
# Evitamos división por cero
if maximo == minimo:
return [0.5 for _ in datos]
return [(x - minimo) / (maximo - minimo) for x in datos]
# Uso
temperaturas = [5, 10, 15, 20, 25]
temperaturas_normalizadas = normalizar_datos(temperaturas)
# [0.0, 0.25, 0.5, 0.75, 1.0]
Esta función es pura porque no modifica los datos originales y siempre devuelve el mismo resultado para la misma entrada.
Limitaciones prácticas
Aunque las funciones puras son ideales, en aplicaciones reales a menudo necesitamos interactuar con el mundo exterior (bases de datos, archivos, APIs). Una estrategia común es:
- Mantener el núcleo de la lógica de negocio en funciones puras
- Aislar las operaciones impuras (E/S) en capas específicas
- Minimizar el alcance de los efectos secundarios
# Capa de E/S (impura)
def obtener_datos_usuario(usuario_id):
return database.query(f"SELECT * FROM usuarios WHERE id = {usuario_id}")
# Capa de lógica de negocio (pura)
def calcular_descuento(historial_compras, total_actual):
if sum(compra['total'] for compra in historial_compras) > 1000:
return total_actual * 0.1
return 0
# Capa de coordinación
def procesar_compra(usuario_id, total):
# Parte impura
historial = obtener_datos_usuario(usuario_id)
# Parte pura
descuento = calcular_descuento(historial, total)
# Parte impura
database.execute(f"UPDATE usuarios SET descuento = {descuento} WHERE id = {usuario_id}")
return total - descuento
Esta separación nos permite aprovechar los beneficios de las funciones puras donde más importa, manteniendo la capacidad de interactuar con sistemas externos.
Inmutabilidad
La inmutabilidad es un principio fundamental del paradigma funcional que complementa el concepto de funciones puras. En esencia, un objeto inmutable es aquel cuyo estado no puede ser modificado después de su creación. En lugar de cambiar objetos existentes, en programación funcional creamos nuevas versiones con las modificaciones deseadas.
Python ofrece tanto estructuras de datos mutables (listas, diccionarios, conjuntos) como inmutables (tuplas, cadenas, números). Entender y aprovechar la inmutabilidad nos permite escribir código más predecible y menos propenso a errores.
Tipos inmutables en Python
Python incluye varios tipos de datos inmutables por defecto:
- Números (int, float, complex)
- Cadenas (str)
- Tuplas (tuple)
- Frozensets
- Bytes
Veamos cómo se comportan estos tipos inmutables:
# Las cadenas son inmutables
nombre = "Python"
# nombre[0] = "J" # Esto generaría un TypeError
# Para "modificar" una cadena, realmente creamos una nueva
nuevo_nombre = "J" + nombre[1:] # Creamos "Jython"
# Las tuplas también son inmutables
coordenadas = (10, 20)
# coordenadas[0] = 15 # Esto generaría un TypeError
Ventajas de la inmutabilidad
La inmutabilidad ofrece varios beneficios importantes:
- Seguridad en concurrencia: Los objetos inmutables son seguros para compartir entre hilos sin necesidad de mecanismos de bloqueo.
- Predictibilidad: El estado de un objeto inmutable no cambiará inesperadamente.
- Hashabilidad: Los objetos inmutables pueden usarse como claves en diccionarios o elementos en conjuntos.
- Razonamiento más sencillo: Es más fácil razonar sobre código que no modifica el estado existente.
Trabajando con colecciones inmutables
Las tuplas son la colección inmutable más común en Python:
# Tupla como estructura de datos inmutable
punto = (3, 4)
distancia = (punto[0]**2 + punto[1]**2)**0.5 # Calculamos sin modificar
# Tuplas con nombres de campos para mayor claridad
from collections import namedtuple
Punto = namedtuple('Punto', ['x', 'y'])
p = Punto(3, 4)
print(p.x, p.y) # Acceso por nombre: 3 4
Para conjuntos inmutables, podemos usar frozenset
:
# Conjunto normal (mutable)
colores = {"rojo", "verde", "azul"}
colores.add("amarillo") # Esto funciona
# Conjunto inmutable
colores_fijos = frozenset(["rojo", "verde", "azul"])
# colores_fijos.add("amarillo") # Esto generaría un AttributeError
Transformaciones inmutables
Cuando trabajamos con inmutabilidad, en lugar de modificar estructuras existentes, creamos nuevas versiones:
# Enfoque imperativo (mutable)
def agregar_impuesto_mutable(productos):
for producto in productos:
producto['precio'] *= 1.21
return productos
# Enfoque funcional (inmutable)
def agregar_impuesto_inmutable(productos):
return [
{**producto, 'precio': producto['precio'] * 1.21}
for producto in productos
]
# Uso
productos_originales = [{'nombre': 'Laptop', 'precio': 1000},
{'nombre': 'Mouse', 'precio': 20}]
# La versión mutable modifica la lista original
productos_con_impuesto = agregar_impuesto_mutable(productos_originales)
# productos_originales ahora está modificado
# Reiniciamos para el ejemplo inmutable
productos_originales = [{'nombre': 'Laptop', 'precio': 1000},
{'nombre': 'Mouse', 'precio': 20}]
# La versión inmutable crea una nueva lista
productos_con_impuesto = agregar_impuesto_inmutable(productos_originales)
# productos_originales permanece intacto
Patrones para mantener la inmutabilidad
Existen varios patrones que nos ayudan a trabajar con inmutabilidad en Python:
- 1. Copiar antes de modificar:
# Con listas
numeros = [1, 2, 3, 4, 5]
numeros_duplicados = [n * 2 for n in numeros] # Nueva lista
# Con diccionarios
config = {'debug': True, 'timeout': 30}
config_actualizada = {**config, 'timeout': 60} # Nuevo diccionario
- 2. Usar métodos que devuelven nuevas instancias:
texto = "hola mundo"
texto_mayusculas = texto.upper() # Devuelve una nueva cadena
numeros = (1, 2, 3)
mas_numeros = numeros + (4, 5) # Devuelve una nueva tupla
- 3. Funciones de transformación:
def incrementar(x):
return x + 1
numeros = [1, 2, 3]
incrementados = list(map(incrementar, numeros)) # Nueva lista [2, 3, 4]
Inmutabilidad en clases personalizadas
Podemos crear nuestras propias clases inmutables en Python:
class Rectangulo:
def __init__(self, ancho, alto):
self._ancho = ancho
self._alto = alto
@property
def ancho(self):
return self._ancho
@property
def alto(self):
return self._alto
@property
def area(self):
return self._ancho * self._alto
def redimensionar(self, nuevo_ancho, nuevo_alto):
# En lugar de modificar, devolvemos una nueva instancia
return Rectangulo(nuevo_ancho, nuevo_alto)
# Uso
rect = Rectangulo(10, 20)
rect_grande = rect.redimensionar(20, 30) # Nueva instancia
Inmutabilidad con dataclasses
Las dataclasses de Python (disponibles desde Python 3.7) facilitan la creación de clases inmutables:
from dataclasses import dataclass
@dataclass(frozen=True) # El parámetro frozen=True hace la clase inmutable
class Punto3D:
x: float
y: float
z: float
def distancia_al_origen(self):
return (self.x**2 + self.y**2 + self.z**2)**0.5
# Uso
p = Punto3D(3, 4, 5)
# p.x = 10 # Esto generaría un error por ser inmutable
Inmutabilidad y rendimiento
La inmutabilidad puede tener implicaciones en el rendimiento:
# Operaciones con cadenas (inmutables)
def construir_cadena_ineficiente(n):
resultado = ""
for i in range(n):
resultado += str(i) # Crea una nueva cadena en cada iteración
return resultado
# Mejor enfoque para cadenas
def construir_cadena_eficiente(n):
partes = []
for i in range(n):
partes.append(str(i))
return "".join(partes) # Una sola operación de concatenación
Para colecciones grandes que requieren muchas modificaciones, a veces es más eficiente usar estructuras mutables durante el procesamiento y convertir a inmutables al final:
# Procesamiento eficiente con enfoque híbrido
def procesar_datos(datos_entrada):
# Usamos una estructura mutable durante el procesamiento
resultados = []
for dato in datos_entrada:
# Varias operaciones de procesamiento
valor_procesado = dato * 2 + 1
resultados.append(valor_procesado)
# Convertimos a inmutable al finalizar
return tuple(resultados)
Inmutabilidad y bibliotecas externas
Algunas bibliotecas de Python promueven la inmutabilidad para facilitar la programación funcional:
# Ejemplo con pyrsistent para estructuras de datos inmutables persistentes
from pyrsistent import pvector, pmap
# Vector inmutable
v1 = pvector([1, 2, 3])
v2 = v1.append(4) # Crea un nuevo vector
# v1 sigue siendo [1, 2, 3]
# v2 es [1, 2, 3, 4]
# Mapa inmutable
m1 = pmap({"a": 1, "b": 2})
m2 = m1.set("c", 3) # Crea un nuevo mapa
# m1 sigue siendo {"a": 1, "b": 2}
# m2 es {"a": 1, "b": 2, "c": 3}
Inmutabilidad y funciones de orden superior
La inmutabilidad se combina perfectamente con funciones de orden superior como map
, filter
y reduce
:
from functools import reduce
# Transformación de datos inmutable con map
numeros = [1, 2, 3, 4, 5]
cuadrados = list(map(lambda x: x**2, numeros)) # [1, 4, 9, 16, 25]
# Filtrado inmutable
pares = list(filter(lambda x: x % 2 == 0, numeros)) # [2, 4]
# Reducción inmutable
suma = reduce(lambda x, y: x + y, numeros) # 15
La inmutabilidad es un pilar fundamental de la programación funcional que, junto con las funciones puras, nos permite escribir código más predecible, seguro y fácil de razonar. Aunque Python no impone la inmutabilidad como otros lenguajes funcionales, nos proporciona las herramientas necesarias para aplicar este principio cuando sea beneficioso.
Funciones como objetos
En Python, las funciones son objetos de primera clase, lo que significa que pueden ser tratadas como cualquier otro tipo de dato. Esta característica es fundamental para el paradigma de programación funcional y diferencia a Python de lenguajes puramente imperativos. Entender este concepto abre un mundo de posibilidades para escribir código más elegante y expresivo.
Funciones como valores
A diferencia de algunos lenguajes donde las funciones son solo bloques de código, en Python podemos:
- Asignar funciones a variables
- Pasar funciones como argumentos
- Devolver funciones desde otras funciones
- Almacenar funciones en estructuras de datos
Veamos un ejemplo sencillo:
def saludar(nombre):
return f"Hola, {nombre}!"
# Asignamos la función a una variable
mi_funcion = saludar
# Usamos la variable como si fuera la función original
resultado = mi_funcion("Ana")
print(resultado) # Imprime: Hola, Ana!
Observa que al asignar saludar
a mi_funcion
, no usamos paréntesis. Esto es porque queremos la función en sí misma, no el resultado de ejecutarla.
Funciones como argumentos
Una de las aplicaciones más potentes es pasar funciones como argumentos a otras funciones:
def aplicar_operacion(func, valor):
return func(valor)
def duplicar(x):
return x * 2
def cuadrado(x):
return x ** 2
# Pasamos diferentes funciones como primer argumento
resultado1 = aplicar_operacion(duplicar, 5) # 10
resultado2 = aplicar_operacion(cuadrado, 5) # 25
Este patrón es la base de muchas operaciones de orden superior en programación funcional, como map
, filter
y reduce
.
Funciones anónimas (lambda)
Python permite crear funciones sin nombre mediante la expresión lambda
. Son útiles cuando necesitamos una función simple para usar una sola vez:
# Función lambda que suma dos números
sumar = lambda x, y: x + y
print(sumar(3, 4)) # 7
# Uso de lambda directamente como argumento
numeros = [1, 5, 3, 9, 2, 6]
ordenados = sorted(numeros, key=lambda x: abs(x - 5))
print(ordenados) # [5, 6, 3, 9, 2, 1] (ordenados por cercanía al número 5)
Las funciones lambda están limitadas a una sola expresión, pero son muy útiles para operaciones simples sin necesidad de definir una función completa.
Funciones de orden superior
Las funciones de orden superior son aquellas que toman otras funciones como argumentos o devuelven funciones. Python incluye varias funciones de orden superior en su biblioteca estándar:
- map(): Aplica una función a cada elemento de un iterable
numeros = [1, 2, 3, 4, 5]
cuadrados = map(lambda x: x**2, numeros)
print(list(cuadrados)) # [1, 4, 9, 16, 25]
- filter(): Filtra elementos de un iterable según una función de predicado
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pares = filter(lambda x: x % 2 == 0, numeros)
print(list(pares)) # [2, 4, 6, 8, 10]
- functools.reduce(): Reduce un iterable a un solo valor aplicando una función acumulativa
from functools import reduce
numeros = [1, 2, 3, 4, 5]
producto = reduce(lambda x, y: x * y, numeros)
print(producto) # 120 (1*2*3*4*5)
Closures: funciones que recuerdan su contexto
Un closure es una función que "recuerda" el entorno en el que fue creada, incluso cuando se ejecuta fuera de ese entorno:
def crear_multiplicador(factor):
# La función interna "recuerda" el valor de factor
def multiplicar(numero):
return numero * factor
return multiplicar
duplicar = crear_multiplicador(2)
triplicar = crear_multiplicador(3)
print(duplicar(5)) # 10
print(triplicar(5)) # 15
En este ejemplo, duplicar
y triplicar
son closures que "recuerdan" los valores de factor
con los que fueron creados.
Decoradores: modificando el comportamiento de funciones
Los decoradores son una aplicación poderosa de las funciones como objetos. Permiten modificar o extender el comportamiento de otras funciones:
def registrar_llamada(func):
def wrapper(*args, **kwargs):
print(f"Llamando a {func.__name__} con {args} y {kwargs}")
resultado = func(*args, **kwargs)
print(f"La función {func.__name__} devolvió {resultado}")
return resultado
return wrapper
@registrar_llamada
def suma(a, b):
return a + b
resultado = suma(3, 5)
# Imprime:
# Llamando a suma con (3, 5) y {}
# La función suma devolvió 8
El decorador @registrar_llamada
es azúcar sintáctico para suma = registrar_llamada(suma)
.
Funciones parciales
La biblioteca functools
proporciona partial
, que permite crear nuevas funciones fijando algunos argumentos de una función existente:
from functools import partial
def potencia(base, exponente):
return base ** exponente
# Creamos una función que eleva al cuadrado
al_cuadrado = partial(potencia, exponente=2)
# Creamos una función que calcula la raíz cuadrada
raiz_cuadrada = partial(potencia, exponente=0.5)
print(al_cuadrado(5)) # 25
print(raiz_cuadrada(25)) # 5.0
Atributos de funciones
Como las funciones son objetos, pueden tener atributos:
def contador():
contador.llamadas += 1
return contador.llamadas
# Inicializamos el atributo
contador.llamadas = 0
print(contador()) # 1
print(contador()) # 2
print(contador()) # 3
print(contador.llamadas) # 3
Funciones como elementos de estructuras de datos
Podemos almacenar funciones en estructuras de datos como listas o diccionarios:
def suma(a, b): return a + b
def resta(a, b): return a - b
def multiplicacion(a, b): return a * b
def division(a, b): return a / b if b != 0 else "Error: División por cero"
# Diccionario de operaciones
operaciones = {
'+': suma,
'-': resta,
'*': multiplicacion,
'/': division
}
# Calculadora simple
def calcular(a, op, b):
if op in operaciones:
return operaciones[op](a, b)
return "Operación no soportada"
print(calcular(10, '+', 5)) # 15
print(calcular(10, '*', 5)) # 50
print(calcular(10, '/', 0)) # Error: División por cero
Este patrón es útil para implementar tablas de dispatch o estrategias intercambiables.
Composición de funciones
La composición de funciones es una técnica donde el resultado de una función se pasa como entrada a otra:
def componer(f, g):
"""Crea una nueva función que aplica f después de g."""
return lambda x: f(g(x))
# Funciones simples para componer
def duplicar(x): return x * 2
def incrementar(x): return x + 1
# Componemos las funciones
duplicar_y_luego_incrementar = componer(incrementar, duplicar)
incrementar_y_luego_duplicar = componer(duplicar, incrementar)
print(duplicar_y_luego_incrementar(5)) # 11 (5*2 + 1)
print(incrementar_y_luego_duplicar(5)) # 12 ((5+1) * 2)
Para componer múltiples funciones, podemos crear un helper más general:
def componer_multiples(*funciones):
"""Compone múltiples funciones de derecha a izquierda."""
def compuesta(x):
resultado = x
for f in reversed(funciones):
resultado = f(resultado)
return resultado
return compuesta
def cuadrado(x): return x ** 2
def duplicar(x): return x * 2
def incrementar(x): return x + 1
# Componemos tres funciones
pipeline = componer_multiples(cuadrado, duplicar, incrementar)
# Equivalente a cuadrado(duplicar(incrementar(5)))
print(pipeline(5)) # 144 ((5+1)*2)^2
Currificación
La currificación es una técnica donde una función que toma múltiples argumentos se transforma en una secuencia de funciones que toman un solo argumento:
def curry(func):
"""Currifica una función de dos argumentos."""
def curried(x):
def inner(y):
return func(x, y)
return inner
return curried
# Función normal de dos argumentos
def multiplicar(x, y):
return x * y
# Versión currificada
multiplicar_currificado = curry(multiplicar)
# Uso
por_cinco = multiplicar_currificado(5)
print(por_cinco(3)) # 15
print(por_cinco(7)) # 35
La currificación facilita la creación de funciones especializadas a partir de funciones más generales.
Aplicaciones prácticas
Veamos un ejemplo práctico que combina varios conceptos:
# Procesamiento de datos con programación funcional
datos = [
{"nombre": "Ana", "edad": 25, "ciudad": "Madrid"},
{"nombre": "Juan", "edad": 17, "ciudad": "Barcelona"},
{"nombre": "María", "edad": 30, "ciudad": "Madrid"},
{"nombre": "Pedro", "edad": 22, "ciudad": "Valencia"},
{"nombre": "Lucía", "edad": 16, "ciudad": "Barcelona"}
]
# Funciones de filtrado
def es_mayor_de_edad(persona):
return persona["edad"] >= 18
def vive_en(ciudad):
# Retornamos una función que comprueba la ciudad
return lambda persona: persona["ciudad"] == ciudad
# Funciones de transformación
def extraer_nombre(persona):
return persona["nombre"]
def saludar(nombre):
return f"Hola, {nombre}!"
# Combinamos todo para obtener saludos a mayores de edad de Madrid
from functools import reduce
resultado = (
# Filtramos mayores de edad
filter(es_mayor_de_edad,
# Filtramos residentes de Madrid
filter(vive_en("Madrid"), datos))
)
# Extraemos nombres y generamos saludos
saludos = map(saludar, map(extraer_nombre, resultado))
for saludo in saludos:
print(saludo)
# Imprime:
# Hola, Ana!
# Hola, María!
Este enfoque permite construir pipelines de procesamiento de datos claros y modulares, donde cada función tiene una responsabilidad única.
El tratamiento de funciones como objetos de primera clase es uno de los pilares que hace que Python sea un lenguaje versátil, permitiendo combinar paradigmas de programación y adoptar un estilo funcional cuando resulta beneficioso para la claridad y mantenibilidad del código.
Ejercicios de esta lección Paradigma funcional
Evalúa tus conocimientos de esta lección Paradigma funcional con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender qué son las funciones puras y sus propiedades fundamentales.
- Aprender a aplicar la inmutabilidad en estructuras de datos y clases en Python.
- Entender cómo las funciones son objetos de primera clase y cómo utilizarlas como valores, argumentos y en composiciones.
- Conocer técnicas funcionales avanzadas como closures, decoradores, currificación y funciones de orden superior.
- Aplicar estos conceptos para escribir código más predecible, modular y fácil de mantener.