2016-05-17 19 views
5

Mam kod blokujący, non-asynchroniczny tak:Lazy iteratory (generatory) z asyncio

def f(): 
    def inner(): 
     while True: 
      yield read() 
    return inner() 

Z tym kodem dzwoniący może wybrać, kiedy zatrzymać funkcję generowania danych. Jak zmienić to na asynchroniczne? To rozwiązanie nie działa:

async def f(): 
    async def inner(): 
     while True: 
      yield await coroutine_read() 
    return inner() 

yield ... bo nie może być stosowany w async def funkcji. Jeśli usuniemy async z podpisu inner(), nie będę już mógł używać await.

+0

Nie potrzebujesz 'yield read()', gdy używasz asyncio, ponieważ 'await' zrobi dokładnie to za kulisami. To oczywiście nie odpowiada na pytanie. –

+1

PEP-0492 [nie obejmuje] (https://www.python.org/dev/peps/pep-0492/#coroutine-generators) generatorów koroutynowych (czego się chce), więc od czasu PEP został zaimplementowany tylko w 3.5, myślę, że odpowiedź brzmi "nie ma sposobu, aby to zrobić". –

+1

Próba wdrożenia sposobu uzyskiwania wewnętrznych funkcji asynchronicznych: http://stackoverflow.com/a/37572657/1113207 –

Odpowiedz

5

Jak wspomniano powyżej, nie można używać yield w funkcjach async. Jeśli chcesz utworzyć coroutine-generator trzeba to zrobić ręcznie, przy użyciu __aiter__ i __anext__ magicznych metod:

import asyncio 


# `coroutine_read()` generates some data: 
i = 0 
async def coroutine_read(): 
    global i 
    i += 1 
    await asyncio.sleep(i) 
    return i 


# `f()` is asynchronous iterator. 
# Since we don't raise `StopAsyncIteration` 
# it works "like" `while True`, until we manually break. 
class f: 
    async def __aiter__(self): 
     return self 

    async def __anext__(self): 
     return await coroutine_read() 


# Use f() as asynchronous iterator with `async for`: 
async def main(): 
    async for i in f(): 
     print(i) 
     if i >= 3: 
      break 


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

wyjściowa:

1 
2 
3 
[Finished in 6.2s] 

Możesz także zobaczyć other post, gdzie StopAsyncIteration zastosowań.

Upd:

Począwszy od Pythona 3.6 mamy asynchronous generators i móc korzystać yield bezpośrednio wewnątrz współprogram.