Python 3, an Illustrated Tour Transcripts
Chapter: Asynchronous Programming
Lecture: asyncio Generators
0:01 In this video, we're going to talk about asynchronous generators.
0:03 These are described in pep 525 Python 3.6
0:06 in the pep it describes some of the motivation for this
0:10 we'll just read this because I think it's pretty interesting.
0:12 It says, however currently there is no equivalent concept
0:15 for the asynchronous iteration protocol async for.
0:18 This makes writing asynchronous data producers unnecessarily complex
0:22 as one must define a class that implements __aiter__ and __anext__
0:27 to be able to use it in async for statement.
0:30 So we saw how to implement a asynchronous iterator
0:33 and we saw that you can do that by implementing __aiter__ and __anext__
0:37 if you've done that with normal iterators in Python,
0:40 you'll note that typically it's a lot easier to do that with the generator
0:43 and this was the same motivation here.
0:46 Also interesting to notice this next paragraph that's in the pep.
0:49 It says performance is an additional point for this proposal
0:51 in our testing of the reference implementation
0:54 asynchronous generators are 2x faster than the equivalent
0:57 implemented as an asynchronous iterator.
1:00 Kind of cool, you get some speed benefit and it's easier to write.
1:03 So here's an example of the migration path.
1:06 We are taking an asynchronous iterator
1:09 and we're starting to add a generator to it.
1:12 Here I've got a class called GenRange,
1:14 and this is, again, similar to that Arange guy that we implemented before
1:17 you can give it a start and an optional end
1:19 and if you don't specify the end it will use start as the end.
1:22 And then we notice that we have down below specified __aiter__
1:27 and in there, note that we are yielding the results,
1:31 we're just looping over range and yielding the result.
1:34 So we're combining a generator with an iterator.
1:37 Here's an example of running this
1:39 we can put it in a coroutine and because this is a coroutine itself
1:44 we need to put async in front of our for when we loop over it
1:48 and this will print the numbers from 0 up to but not including 5.
1:51 Now we want to take that a step further
1:53 we can do the same thing with normal iterators.
1:56 We can yield from __iter__ in there,
1:59 but wouldn't it be nicer if we could just make a generator function
2:02 that is a generator and we can do that,
2:04 here we have a function that is a generator
2:06 because it has a yield in it but it also has an async in front of it.
2:10 So this is a synchronous generator.
2:12 And this is the same implementation that we had before
2:14 but note that the logic is a lot simpler
2:18 and we don't have to keep track of state
2:20 because this freezes and comes back to itself like a normal generator in Python would,
2:25 so here's an example of using it, again because it is a coroutine
2:29 it behaves as a coroutine when we loop over it
2:32 we need to put that async in front of our for there,
2:34 but this will print the numbers from 0 up to but not including 5,
2:38 so that's how you can make a asynchronous genera tor in Python.
2:41 Just put an async in front of your def there
2:45 and include a yield in your logic
2:47 and you now have an asynchronous generator.
2:50 Hopefully, you can use these to make your code more succinct and more legible.