Serie de promesas en Python

Abdul Mateen 21 junio 2023
  1. Programación asíncrona en Python
  2. Función de devolución de llamada
  3. intentar/excepto en Python
  4. Serie de promesas en Python
Serie de promesas en Python

Este tutorial nos enseñará cómo escribir una serie de promesas en Python. Primero, discutiremos la programación asíncrona en Python.

A continuación, discutiremos la función de devolución de llamada en Python. Finalmente, antes de llegar al tema real, discutiremos brevemente probar/excepto en Python y luego llegar a una serie de promesas en Python.

Programación asíncrona en Python

Este artículo espera que tenga una idea básica de los subprocesos del sistema operativo. Si no tiene una introducción preliminar a los subprocesos, puede leer subprocesos en sistemas operativos como requisito previo.

La programación asincrónica permite que varios subprocesos se ejecuten en paralelo, mientras que el programa principal (normalmente denominado subproceso principal/administrador) puede crear varios subprocesos de trabajo. Por lo general, los subprocesos principales esperan subprocesos de trabajo, que informan al subproceso principal después de completar la tarea.

A diferencia de la programación regular, en lugar de mantener el control hasta la competencia, la función asíncrona hace una pausa y permite la ejecución de otras funciones (hilos) en paralelo.

Discutiremos y daremos un ejemplo de programación asíncrona en Python; sin embargo, es mejor ver primero un código síncrono relacionado. Este código ayudará a desarrollar la comprensión por comparación.

def count():
    for i in range(5):
        print(i, end=" ")


def main():
    count()
    count()
    count()


main()

Aquí, llamamos a la función contar tres veces seguidas. La salida es según las expectativas.

0 1 2 3 4 0 1 2 3 4 0 1 2 3 4

Puede ver la salida de la primera función contar, seguida de la salida de la segunda llamada de la función contar, seguida de la salida de la última función contar.

La biblioteca asyncio de Python permite ejecutar programas asíncronos en Python. El primer requisito para la programación asincrónica es diseñar funciones como objetos aguardables.

Hay dos requisitos para convertir una función estándar en un objeto awaitable. El primero es usar la palabra clave async (antes de la palabra clave def) para crear funciones asíncronas en lugar de funciones de rutina.

El segundo requisito es llamar a la función sleep dentro de funciones asíncronas, suspender la función actual y controlar otras funciones.

La instrucción sleep es el punto específico en el código donde la función pasa exactamente al estado de suspensión. El segundo requisito de la programación asincrónica es agregar await al llamar a los objetos awaitable (funciones asincrónicas); de lo contrario, habrá un error.

La palabra clave await le dice al bucle de eventos que suspenda la función actual para dar tiempo de ejecución a otras funciones.

El tercer requisito es llamar a la función gather y pasar objetos awaitable (funciones asíncronas). La función reunir ejecuta estas funciones en su orden pero simultáneamente.

Significa que la primera función comienza primero y, después de un tiempo, la segunda función también comienza en paralelo. De manera similar, todas las funciones asincrónicas comienzan a ejecutarse simultáneamente, una por una.

Ahora, veamos el código.

import asyncio


async def count():
    for i in range(5):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def main():
    await asyncio.gather(count(), count(), count())


if __name__ == "__main__":

    asyncio.run(main())

Aquí, hemos convertido nuestro código anterior en código asíncrono con ciertas adiciones. En la primera línea se importa la librería asyncio.

La palabra clave async se agrega al comienzo de todas las funciones.

A la función contar se añade la llamada a la función sleep. La palabra clave esperar se agrega con todas las llamadas a funciones, incluida la función main.

Por último, la función reunir se llama en principal, donde la función contar se llama varias veces para demostrar cada llamada de función como un hilo separado.

Usando la función reunir, agregamos objetos disponibles para formar un grupo de funciones asíncronas para ejecutarse simultáneamente. Veamos la salida de este código.

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4

En la salida, puede ver que todos los subprocesos se ejecutan en paralelo y producen una salida asíncrona en lugar de ejecutarse por completo.

Puede confundirse con llamar a la misma función varias veces; aquí hay otro ejemplo donde tenemos diferentes funciones ejecutándose en paralelo.

import asyncio


async def count1():
    for i in range(10):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def count2():
    for i in range(50, 60):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def main():
    await asyncio.gather(count1(), count2())


asyncio.run(main())

La salida de este código es:

0 50 1 51 2 52 3 53 4 54 5 55 6 56 7 57 8 58 9 59

Una vez más, ambas funciones se ejecutan simultáneamente.

Función de devolución de llamada

Se pasa una devolución de llamada a otra función (como un argumento). Se supone que la otra función devolverá la llamada a esta función en algún lugar de su definición.

Sin embargo, el punto de llamada depende de cómo se defina otra función.

Aquí, tenemos un ejemplo de codificación sencillo relacionado con la función de devolución de llamada.

import random as r


def callback1(s):
    print(f"******* {s} *******")


def callback2(s):
    print(f"^^^^^^^ {s} ^^^^^^^")


def print_through_callback(message, f1, f2):
    if r.randint(0, 1) == 0:
        f1(message)
    else:
        f2(message)


def main():

    print_through_callback("Callback Example", callback1, callback2)


main()

En este código, nuestra función print tiene tres parámetros. El segundo y tercer parámetro son algunos nombres de funciones.

En el principal, pasamos dos funciones y el código llama aleatoriamente a una. Si ejecuta este código varias veces, puede ver que ambas funciones se llaman aleatoriamente.

intentar/excepto en Python

Python también ofrece manejo de excepciones. En Python tenemos un bloque try para probar el código; tiene el potencial para una excepción, y en el bloque excepto, puede manejar la excepción.

Todos sabemos que dividir por cero no está definido y que los programas (en casi todos los lenguajes de programación) fallan; cuando llamamos a la operación dividir por cero. Si no tienes idea, prueba este código.

def main():
    x = int(input("Enter any number:"))
    print(2 / x)


main()

Ingrese cero y vea el resultado; su programa se bloqueará. El bloqueo del código es algo malo y debe evitarse mediante el manejo de excepciones.

Vea el mismo código con el manejo de excepciones.

def main():
    try:
        x = int(input("Enter any number:"))
        print(2 / x)
    except:
        print("Divide by zero is not defined")


main()

Debe ejecutar este código e ingresar valores distintos de cero; obtendrás el resultado de la operación de división, escrito dentro de print (2/x); si ingresa cero, el programa dará el mensaje “Dividir por cero no está definido” en lugar de un bloqueo.

Serie de promesas en Python

Las funciones de devolución de llamada son las mismas que las funciones normales; sin embargo, su uso difiere.

Considere funciones pesadas que requieren mucho tiempo para ejecutarse. Por lo general, tales funciones se hacen asincrónicas.

Las funciones asincrónicas se ejecutarán en segundo plano y, después de un tiempo específico, se completarán, mientras que otras funciones se iniciarán en paralelo. Sin embargo, si desea ejecutar alguna función después de completar alguna función pesada, la elección es utilizar la función de devolución de llamada.

Sin embargo, hay un problema con la realización de tales tareas. ¿Qué sucede si la tarea arroja una excepción antes de completarse?

Para garantizar que la función se llame después de completar con éxito la tarea, se necesitan promesas y programación asincrónica.

promesas

Una promesa es un objeto que representa la finalización exitosa o no exitosa (falla) de una función asincrónica.

Los objetos de promesa también representan el valor resultante de la función asíncrona. Se utiliza una promesa para gestionar los problemas relacionados con múltiples devoluciones de llamada.

Puede usar promesa API para ejecutar una serie de promesas en Python. Sin embargo, podemos lograr el mismo propósito en Python a través de async/await.

Para la implementación en Python con funciones asíncronas, tenemos que usar la librería asyncio con funciones asíncronas. Podemos llamar funciones en secuencia con la palabra clave await, que ya se describió anteriormente.

Finalmente, estamos usando el bloque try/except. Primero, vea el código y la salida.

Más adelante, explicaremos el propósito del bloque try/except.

import asyncio
import random as r


async def f1(x):
    await asyncio.sleep(1)
    return x ** 2


async def f2(x):
    await asyncio.sleep(1)
    return x / 2


async def f3(x):
    if r.randint(0, 1) == 0:
        return x
    raise ValueError(x)


async def run():
    try:
        value = await f3(await f2(await f1(r.randint(5, 9))))
    except ValueError as exception:
        print("Exception Occurred:", exception.args[0])
    else:
        print("No Exception:", value)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
    loop.close()

El siguiente es el resultado combinado de la secuencia de comandos de Python anterior durante 5 ejecuciones.

No Exception: 12.5
Exception Occurred: 12.5
Exception Occurred: 32.0
No Exception: 18.0
No Exception: 40.5

La salida solo describe que podemos tener éxito o fallar en alguna operación asíncrona. Por lo tanto, podemos colocar una declaración para la función de devolución de llamada después de la llamada de función en el bloque intentar.

En caso de completarse con éxito, el código ejecutará la función de devolución de llamada. En caso de falla, el control irá al bloque excepto e ignorará la función de devolución de llamada.

De esta forma podemos manejar la serie de promesas en Python; si tenemos una finalización exitosa, ejecute la función de devolución de llamada (que depende de la finalización exitosa de alguna tarea requerida); de lo contrario, no tenemos que ejecutar nuestra función de devolución de llamada.