EAS Latam Academy Logo EAS Latam

Módulo 3: Python Avanzado

Decoradores

Los decoradores son una de las características más elegantes y potentes de Python. Permiten modificar o extender el comportamiento de funciones o clases sin alterar su código fuente.

Fundamentos de los Decoradores

Un decorador es esencialmente una función que toma otra función como argumento, le añade alguna funcionalidad y la devuelve.

def mi_decorador(func):
    def wrapper(*args, **kwargs):
        print("Antes de llamar a la función.")
        resultado = func(*args, **kwargs)
        print("Después de llamar a la función.")
        return resultado
    return wrapper

@mi_decorador
def saludar(nombre):
    print(f"Hola, {nombre}!")

saludar("Alice")
# Salida:
# Antes de llamar a la función.
# Hola, Alice!
# Después de llamar a la función.

Decoradores con Argumentos

Los decoradores pueden aceptar argumentos adicionales, lo que añade flexibilidad.

def repetir(veces):
    def decorador(func):
        def wrapper(*args, **kwargs):
            for _ in range(veces):
                func(*args, **kwargs)
        return wrapper
    return decorador

@repetir(veces=3)
def mensaje():
    print("Este mensaje se repite.")

mensaje()
# Salida:
# Este mensaje se repite.
# Este mensaje se repite.
# Este mensaje se repite.

Generadores e Iteradores

Los generadores son funciones que pueden pausar y reanudar su ejecución, produciendo una secuencia de valores lazy (uno a la vez, bajo demanda). Son muy eficientes en memoria para trabajar con grandes conjuntos de datos.

Funciones Generadoras (yield)

def contador_infinito():
    n = 0
    while True:
        yield n
        n += 1

gen = contador_infinito()
print(next(gen)) # 0
print(next(gen)) # 1

Expresiones Generadoras

Son similares a las comprensiones de listas, pero usan paréntesis () y crean un objeto generador.

cuadrados_lazy = (x*x for x in range(1000000))
# Solo se calcula un cuadrado a la vez cuando se itera

Programación Asíncrona (asyncio)

Python ofrece un soporte robusto para la programación asíncrona, lo que permite realizar múltiples operaciones (especialmente E/S) de manera concurrente sin usar múltiples hilos de CPU.

async y await

  • async def: Define una función como una coroutine (corutina).
  • await: Pausa la ejecución de la coroutine hasta que otra coroutine termine.
import asyncio

async def tarea(nombre, duracion):
    print(f"Tarea {nombre}: comenzando...")
    await asyncio.sleep(duracion) # Pausa la coroutine, no el programa entero
    print(f"Tarea {nombre}: terminada después de {duracion} segundos.")

async def main():
    await asyncio.gather(
        tarea("A", 2),
        tarea("B", 1)
    )

if __name__ == "__main__":
    asyncio.run(main())
# Salida (aproximada, B termina antes):
# Tarea A: comenzando...
# Tarea B: comenzando...
# Tarea B: terminada después de 1 segundos.
# Tarea A: terminada después de 2 segundos.

Metaprogramación

La metaprogramación es la capacidad de un programa para tratarse a sí mismo o a otros programas como datos. En Python, esto a menudo implica el uso de type, exec, eval y metaclasses.

Metaclasses

Las metaclasses son las “fábricas” de clases. Permiten controlar cómo se crean las clases.

class MetaMiClase(type):
    def __new__(mcs, name, bases, attrs):
        # Aquí puedes modificar la clase antes de que se cree
        attrs['version'] = '1.0'
        return super().__new__(mcs, name, bases, attrs)

class MiClase(metaclass=MetaMiClase):
    pass

print(MiClase.version) # 1.0

Conclusión

Este módulo ha explorado algunas de las características más avanzadas y poderosas de Python, como los decoradores, generadores, la programación asíncrona y la metaprogramación. Dominar estos conceptos te permitirá escribir código más conciso, eficiente y flexible, abriendo las puertas a la construcción de sistemas complejos y de alto rendimiento.

¿Listo para el desafío?

Has revisado la teoría. Ahora es momento de poner a prueba tus conocimientos.

Iniciar Desafío