2016-05-31 5 views
22

Я хочу использовать выход генератора и асинхронные функции. Я прочитал this topic и написал следующий код:Как использовать 'yield' внутри функции async?

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 

Но я получил ошибку:

SyntaxError: «выход» внутри функции асинхронной

Как использовать генератор выход в функции асинхронной?

+1

Возможно ли это? Это похоже на два противоположных проекта. Генераторы не производят значения, если это необходимо, это означает, что в принципе они должны обрабатывать состояние *. С другой стороны, 'async' предполагает, что вызываемая функция не может зависеть от этого состояния. В противном случае у вас будут гонки данных. Кажется очень громоздким поддерживать асинхронные генераторы, их нужно будет обернуть механизмами блокировки. Поэтому, вероятно, ответ на ваш вопрос находится где-то в этом направлении. – luk32

+0

можете ли вы вернуть объект Future, а затем предоставить этот объект, когда захотите его. Я никогда не использовал асинчо, но так оно и было с Торнадо. – reticentroot

+0

Я не думаю, что генератор асинхронов имеет смысл. Вы должны иметь возможность вернуть генератор из функции async. Есть ли что-то, чего вы хотите достичь, или вы просто пытаетесь разобраться? – syntonym

ответ

29

Upd:

Начиная с Python 3.6 мы имеем asynchronous generators и возможность использовать yield непосредственно внутри сопрограммам.

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.AbstractEventLoop.shutdown_asyncgens 
    loop.close() 

Старый ответ на Python 3.5:

yield Вы не можете внутри сопрограммы. Единственный способ - реализовать Asynchronous Iterator с помощью __aiter__/__anext__ магических методов. В вашем случае:

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()) 

Выход:

0 
1 
4 

Here're еще два примера: 1, 2

+1

На основе вашего Python 3.6 Я сделал пример использования 'matlibplot', если кто-то заинтересован: https://stackoverflow.com/questions/44163601/asyncio-matplotlib-show-still-freezes-program/44175558#44175558 – NumesSanguis

+0

Все еще ждут этого в C# :( – nawfal

4

Новый Python 3.6 поставляется с поддержкой асинхронных генераторов.

PEP 0525

What's new in Python 3.6

PS: На момент написания Python 3.6 все еще бета. Если вы работаете в GNU/Linux или OS X, и вы не можете ждать, вы можете попробовать новый Python с pyenv.

1

Это должно работать с питоном 3.6 (проверено с 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