¿Cómo usar ‘rendimiento’ dentro de la función asíncrona?

3 minutos de lectura

Avatar de usuario de Ильдар
Ильдар

Quiero usar el rendimiento del generador y las funciones asíncronas. Leí este tema y escribí el siguiente código:

import asyncio

async def createGenerator():
    mylist = range(3)
    for i in mylist:
        await asyncio.sleep(1)
        yield i*i

async def start():
    mygenerator = await createGenerator()
    for i in mygenerator:
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass

Pero tengo el error:

SyntaxError: ‘rendimiento’ dentro de la función asíncrona

¿Cómo usar el generador de rendimiento en la función asíncrona?

  • ¿Es eso posible? Parecen dos diseños opuestos. Los generadores están hechos para no producir valor a menos que sea necesario, lo que significa que, en principio, deben manejar tener un Expresar. async por otro lado sugeriría que la función llamada no puede depender de su estado. De lo contrario tendrás carreras de datos. Parece realmente engorroso admitir generadores asíncronos, tendrían que envolverse con mecanismos de bloqueo. Entonces, probablemente la respuesta a su pregunta esté en algún lugar en esta dirección.

    – luk32

    31 mayo 2016 a las 15:39


  • ¿Puedes devolver un objeto futuro y luego ceder ese objeto cuando quieras sus datos? Nunca he usado asyncio, pero así es como se hace con Tornado.

    – raíz reticente

    31 mayo 2016 a las 15:45

  • No creo que un generador asíncrono tenga ningún sentido. Debería poder devolver un generador desde una función asíncrona. ¿Hay algo que quieras lograr o solo estás probando cosas?

    – sinónimo

    31 de mayo de 2016 a las 15:56

  • ¿Se puede usar Evento? createGenerator será un evento de configuración y el inicio será un evento de espera. escribí esta solución. Es trabajo, pero quiero más código agradable.

    – Ильдар

    31 de mayo de 2016 a las 16:03


  • @Ильдар, ¿viste la segunda respuesta? ¿Que piensas de eso? Parece que funciona.

    – Mijaíl Gerasimov

    2 de junio de 2016 a las 12:15

Avatar de usuario de Mikhail Gerasimov
Mijaíl Gerasimov

Actualización:

Comenzando con Python 3.6 tenemos generadores asincrónicos y capaz de usar yield directamente dentro de coroutines.

import asyncio


async def async_generator():
    for i in range(3):
        await asyncio.sleep(1)
        yield i*i


async def main():
    async for i in async_generator():
        print(i)


loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())  # see: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_asyncgens
    loop.close()

Respuesta anterior para Python 3.5:

no puedes yield corrutinas internas. La única manera es implementar Iterador asíncrono usando manualmente __aiter__/__anext__ métodos mágicos. En tu caso:

import asyncio


class async_generator:
    def __init__(self, stop):
        self.i = 0
        self.stop = stop

    async def __aiter__(self):
        return self

    async def __anext__(self):
        i = self.i
        self.i += 1
        if self.i <= self.stop:
            await asyncio.sleep(1)
            return i * i
        else:
            raise StopAsyncIteration


async def main():
    async for i in async_generator(3):
        print(i)


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

Producción:

0
1
4

Aquí hay dos ejemplos más: 1, 2

  • Basado en su código Python 3.6, hice un ejemplo para usar matlibplot por si a alguien le interesa: stackoverflow.com/questions/44163601/…

    – Numes Sanguis

    25 de mayo de 2017 a las 8:14

El nuevo Python 3.6 viene con soporte para generadores asíncronos.

PEP 0525

Novedades en Python 3.6

PD: En el momento de escribir, Python 3.6 aún es beta. Si está en GNU/Linux o OS X y no puede esperar, puede probar el nuevo Python con pyenv.

Esto debería funcionar con python 3.6 (probado con 3.6.0b1):

import asyncio

async def createGenerator():
    mylist = range(3)
    for i in mylist:
        await asyncio.sleep(1)
        yield i*i

async def start():
    async for i in createGenerator():
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass

¿Ha sido útil esta solución?