[Python-ideas] PEP 525: Asynchronous Generators

Yury Selivanov yselivanov.ml at gmail.com
Thu Aug 4 11:17:55 EDT 2016


Hi Sven,

On 2016-08-04 9:20 AM, Sven R. Kunze wrote:
> Hey Yury,
>
> that's a great proposal!
>
>
> On 03.08.2016 00:31, Yury Selivanov wrote:
>>
>>     class Ticker:
>>         """Yield numbers from 0 to `to` every `delay` seconds."""
>>
>>         def __init__(self, delay, to):
>>             self.delay = delay
>>             self.i = 0
>>             self.to = to
>>
>>         def __aiter__(self):
>>             return self
>>
>>         async def __anext__(self):
>>             i = self.i
>>             if i >= self.to:
>>                 raise StopAsyncIteration
>>             self.i += 1
>>             if i:
>>                 await asyncio.sleep(self.delay)
>>             return i
>>
>>
>> The same can be implemented as a much simpler asynchronous generator::
>>
>>     async def ticker(delay, to):
>>         """Yield numbers from 0 to `to` every `delay` seconds."""
>>         for i in range(to):
>>             yield i
>>             await asyncio.sleep(delay)
>>
>
> That's a great motivational example. +1
>
> Especially when reading the venerable PEP 255 (Q/A part), this also 
> gets rid of the "low-level detail": "raise StopAsyncIteration". This 
> could also be worth mentioning in the motivation part for PEP 525.

Thanks!

[..]
> From your answer to Stefan, I get the impression that the reason why 
> we actual need all those a* methods (basically a duplication of the 
> existing gen protocol), is the fact that normal generators can be 
> converted to coroutines. That means, 'yield' still can be used in both 
> ways.
>
> So, it's a technical symptom of the backwards-compatibility rather 
> than something that cannot be avoided by design. Is this correct?
>

async/await in Python is implemented on top of the generator protocol.  
Any 'await' is either awaiting on a coroutine or on a Future-like 
object.  Future-like objects are defined by implementing the __await__ 
method, which should return a generator.

So coroutines and generators are very intimately tied to each other, and 
that's *by design*.

Any coroutine that iterates over an asynchronous generator uses the 
generator protocol behind the scenes.  So we have to multiplex the async 
generaotor's "yields" into the generator protocol in such a way, that it 
stays isolated, and does not interfere with the "yields" that drive 
async/await.

>
>
> If it's correct, would you think it would make sense to get rid of the 
> a* in a later iteration of the async capabilities of Python? So, just 
> using the normal generator protocol again?

Because async generators will contain 'await' expressions, we have to 
have a* methods (although we can name them without the "a" prefix, but I 
believe that would be confusing for many users).

>
>
> One note on all examples but the last. Reading those examples, it 
> creates the illusion of actual working code which is not the case, 
> right? One would always need to 1) wrap module-level statements into 
> its own coroutine, 2) create an event-loop and 3) run it. Do you think 
> clarifying this in the PEP makes sense?

I'll think about this, thanks!  Maybe I can add a line in the beginning 
of the "Specification" section.

>
>
> Thanks again for your hard work here. Async generators definitely 
> completes the picture.

Thank you, Sven

Yury


More information about the Python-ideas mailing list