Cómo usar decoradores de corutinas

PythonPythonBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Este tutorial completo se adentra en el poderoso mundo de los decoradores de corutinas en Python, brindando a los desarrolladores técnicas esenciales para mejorar la programación asíncrona. Al explorar los conceptos básicos de los decoradores y los patrones prácticos de corutinas, los lectores obtendrán información sobre cómo crear soluciones de código asíncrono más eficientes y elegantes.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/lambda_functions("Lambda Functions") python/AdvancedTopicsGroup -.-> python/iterators("Iterators") python/AdvancedTopicsGroup -.-> python/generators("Generators") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") subgraph Lab Skills python/function_definition -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} python/arguments_return -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} python/lambda_functions -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} python/iterators -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} python/generators -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} python/decorators -.-> lab-452350{{"Cómo usar decoradores de corutinas"}} end

Conceptos básicos de las corutinas

¿Qué son las corutinas?

Las corutinas son un poderoso concepto de programación en Python que te permite escribir código concurrente de una manera más legible y eficiente. A diferencia de las funciones tradicionales que se ejecutan hasta completarse, las corutinas pueden pausar y reanudar su ejecución, lo que permite la multitarea cooperativa.

Características clave de las corutinas

Las corutinas ofrecen varias características únicas:

Característica Descripción
Suspensión Pueden pausar y reanudar la ejecución
Preservación de estado Mantienen el estado interno entre llamadas
Ligera Más eficiente en memoria que los hilos
No bloqueante Permite la programación asíncrona

Sintaxis básica de las corutinas

A continuación, se muestra un ejemplo sencillo de una corutina en Python:

async def example_coroutine():
    print("Starting coroutine")
    await asyncio.sleep(1)  ## Simulating an async operation
    print("Coroutine completed")

Visualización del flujo de las corutinas

graph TD A[Start Coroutine] --> B{Async Operation} B --> |Await| C[Suspend Execution] C --> |Resume| D[Continue Execution] D --> E[Complete Coroutine]

Cuándo usar corutinas

Las corutinas son especialmente útiles en escenarios que involucren:

  • Operaciones limitadas por E/S
  • Programación de redes
  • Gestión de tareas concurrentes
  • Programación controlada por eventos

Creación de corutinas con async/await

Las palabras clave async y await son fundamentales para la implementación de corutinas:

import asyncio

async def fetch_data(url):
    print(f"Fetching data from {url}")
    await asyncio.sleep(2)  ## Simulating network delay
    return f"Data from {url}"

async def main():
    result = await fetch_data("https://labex.io")
    print(result)

asyncio.run(main())

Corutinas vs funciones regulares

Aspecto Función regular Corutina
Ejecución Se ejecuta hasta completarse Puede pausar y reanudar
Palabra clave def async def
Llamada Llamada directa Requiere await
Concurrencia Bloqueante No bloqueante

Consideraciones de rendimiento

Si bien las corutinas ofrecen una excelente concurrencia, no son una solución universal. Considera:

  • La sobrecarga del marco asíncrono
  • La complejidad del código asíncrono
  • Los casos de uso adecuados

Al entender estos conceptos básicos, los desarrolladores pueden aprovechar las corutinas para escribir aplicaciones de Python más eficientes y receptivas, especialmente en los entornos de programación avanzados de LabEx.

Conceptos básicos de los decoradores

¿Qué son los decoradores?

Los decoradores son una poderosa característica de Python que te permite modificar o mejorar funciones y métodos sin cambiar directamente su código fuente. Proporcionan una forma limpia y reutilizable de extender la funcionalidad.

Estructura básica de un decorador

def my_decorator(func):
    def wrapper(*args, **kwargs):
        ## Code before function execution
        result = func(*args, **kwargs)
        ## Code after function execution
        return result
    return wrapper

@my_decorator
def example_function():
    pass

Visualización del flujo de un decorador

graph TD A[Original Function] --> B[Decorator Wrapper] B --> C{Pre-processing} C --> D[Original Function Call] D --> E{Post-processing} E --> F[Return Result]

Tipos de decoradores

Tipo de decorador Descripción Caso de uso
Decoradores de función Modifican el comportamiento de una función Registro (logging), medición de tiempo, autenticación
Decoradores de clase Modifican el comportamiento de una clase Patrón singleton, caché
Decoradores de método Mejoran la funcionalidad de un método Validación, control de acceso

Técnicas avanzadas de decoradores

Decoradores parametrizados

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello, {name}!")

Preservación de metadatos

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        """Wrapper function documentation"""
        return func(*args, **kwargs)
    return wrapper

Decoradores específicos para corutinas

Los decoradores pueden ser especialmente poderosos con corutinas:

import asyncio
import time

def timer_decorator(func):
    async def wrapper(*args, **kwargs):
        start = time.time()
        result = await func(*args, **kwargs)
        end = time.time()
        print(f"Execution time: {end - start} seconds")
        return result
    return wrapper

@timer_decorator
async def async_operation():
    await asyncio.sleep(1)
    return "Operation completed"

Patrones comunes de decoradores

Patrón Descripción Ejemplo
Registro (Logging) Realiza un seguimiento de las llamadas a funciones Registrar entrada/salida de un método
Caché Almacena los resultados de una función Memoización
Autenticación Controla el acceso Comprobaciones de permisos de usuario
Reintentos Implementa lógica de reintento Manejar fallos transitorios

Mejores prácticas

  • Mantén los decoradores simples y enfocados.
  • Utiliza functools.wraps para preservar los metadatos de la función.
  • Evita la lógica compleja en los decoradores.
  • Considera las implicaciones de rendimiento.

Consideraciones de rendimiento

Los decoradores añaden una pequeña sobrecarga debido al envoltorio de la función. En código crítico para el rendimiento, utilízalos con moderación.

Al dominar los decoradores, los desarrolladores pueden escribir código más modular y mantenible, una habilidad muy valorada en los entornos de programación avanzados de LabEx.

Patrones prácticos de corutinas

Ejecución concurrente de tareas

Procesamiento de tareas en paralelo

import asyncio

async def fetch_url(url):
    await asyncio.sleep(1)  ## Simulate network request
    return f"Data from {url}"

async def main():
    urls = [
        'https://labex.io/course1',
        'https://labex.io/course2',
        'https://labex.io/course3'
    ]

    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks)

    for result in results:
        print(result)

asyncio.run(main())

Patrones de sincronización de corutinas

Control de semáforos

import asyncio

async def limited_concurrent_tasks():
    semaphore = asyncio.Semaphore(2)

    async def worker(name):
        async with semaphore:
            print(f"Worker {name} started")
            await asyncio.sleep(2)
            print(f"Worker {name} completed")

    tasks = [worker(i) for i in range(5)]
    await asyncio.gather(*tasks)

Visualización del flujo de corutinas

graph TD A[Start Concurrent Tasks] --> B{Semaphore Control} B --> |Limit Concurrency| C[Execute Tasks] C --> D[Wait for Completion] D --> E[Collect Results]

Estrategias de manejo de errores

Gestión robusta de errores en corutinas

import asyncio

async def safe_task(task_id):
    try:
        if task_id == 3:
            raise ValueError("Simulated error")
        await asyncio.sleep(1)
        return f"Task {task_id} completed successfully"
    except Exception as e:
        return f"Task {task_id} failed: {str(e)}"

async def main():
    tasks = [safe_task(i) for i in range(5)]
    results = await asyncio.gather(*tasks, return_exceptions=True)

    for result in results:
        print(result)

asyncio.run(main())

Comparación de patrones de corutinas

Patrón Caso de uso Complejidad Rendimiento
Ejecución concurrente Tareas en paralelo Baja Alto
Control de semáforos Gestión de recursos Media Moderado
Manejo de errores Ejecución robusta de tareas Alta Moderado

Técnicas avanzadas de corutinas

Gestión de tiempos de espera (timeout)

import asyncio

async def task_with_timeout(timeout=2):
    try:
        result = await asyncio.wait_for(
            long_running_task(),
            timeout=timeout
        )
        return result
    except asyncio.TimeoutError:
        return "Task timed out"

async def long_running_task():
    await asyncio.sleep(3)
    return "Completed"

Manipulación del bucle de eventos

Manejo personalizado del bucle de eventos

import asyncio

class AsyncContextManager:
    async def __aenter__(self):
        print("Entering async context")
        return self

    async def __aexit__(self, exc_type, exc, tb):
        print("Exiting async context")

async def main():
    async with AsyncContextManager():
        await asyncio.sleep(1)
        print("Inside context")

asyncio.run(main())

Estrategias de optimización de rendimiento

  • Minimizar las operaciones bloqueantes
  • Utilizar niveles de concurrencia adecuados
  • Aprovechar el eficiente bucle de eventos de asyncio
  • Perfilar y optimizar las rutas críticas

Aplicaciones reales de corutinas

Dominio Uso típico
Web Scraping Recuperación concurrente de datos
Servicios de red Servidores de alto rendimiento
Procesamiento de datos Cómputo en paralelo
Aplicaciones IoT Comunicación eficiente de dispositivos

Al dominar estos patrones prácticos de corutinas, los desarrolladores pueden construir aplicaciones sofisticadas y de alto rendimiento en los entornos de programación avanzados de LabEx.

Resumen

Al dominar los decoradores de corutinas en Python, los desarrolladores pueden mejorar significativamente sus habilidades de programación asíncrona. Este tutorial te ha proporcionado conceptos fundamentales, técnicas de decoradores y patrones prácticos para crear código concurrente más robusto y eficiente, lo que permite desarrollar aplicaciones de Python más sofisticadas y con mejor rendimiento.