Coroutines in Python

The building blocks of asynchronous programming

Robin Siwach
Aug 2, 2020 · 4 min read
Image for post
Image for post
Photo by Michael Fousert on Unsplash.

Prerequisites: You should know about iterators and how a for loop in Python works behind the scenes. You don’t need to know anything about generators, coroutines, or the yield keyword.

Just a quick overview of how a for loop works in Python:

OUTPUT :

The code above is equivalent to the code given below:

OUTPUT :

What Are Coroutines?

Coroutines are basically functions whose execution can be paused/suspended at a particular point, and then we can resume the execution from that same point later whenever we want.

We need a mechanism — or to be more precise, a keyword — by which we can insert a checkpoint and tell the program that we want to pause the execution of the function here and return control to the point it called from. We’ll resume the execution whenever we want.

In Python, we can pause the execution of a function using the yield keyword.

So here’s where things get interesting:

  • We can think of a coroutine as a function that has one or more checkpoints where the execution will be paused and control will be returned to the point where it was called from.
  • In essence, a coroutine is a function divided into many parts, and we can execute each part of a coroutine as we execute each iteration of a for loop using the next function.

Here’s a basic example:

OUTPUT :

From the output, we notice a few things:

  • First, we need to call the coroutine/function that will give us a generator object.
  • That generator object will behave similarly to an iterator, but in the case of an iterator, we are traversing over an iterable. With a generator, we’re executing parts of the coroutine.
  • Just as a StopIteration exception is thrown and caught behind the scenes of a for loop, the same happens in this case when the last part of the coroutine is executed.

Now this pausing of the function in between is very interesting and opens up some possibilities:

  • When the function is paused, we do nothing, which is the case that we just saw.
  • Suppose a variable is being modified several times in a function and we want the value of that particular variable at a certain checkpoint. Then when we pause that function on that particular checkpoint, it returns the value of that variable.

Let’s see an example:

OUTPUT :

Here, the value of x is returned by yield at different checkpoints, as the function execution has been paused.

Whenever we are executing the last part of the function and there is no yield left in the function, after executing that last part, a StopIteration exception will be raised.

Much like when an iterator tries to execute the next function, but there are no more elements left in the iterable, it also raises the StopIteration exception.

  • Suppose we want to send a value (which can be a constant or variable) at a certain checkpoint (i.e. at a certain state of a function). We can also do that using the yield keyword. When we want to send a value, we'll use the send function instead of next.

Let’s see an example:

OUTPUT :

The reason we used next before using send is we can only use send when we are at the yield checkpoint, and yield is on the right side of the expression. So to reach that first yield, we have to use the next function.

Now here comes an interesting application of coroutines. Suppose we want to switch back and forth between two functions like we do in multithreading. In multithreading, until an interrupt is encountered by the OS, it will keep executing. In this case, we can switch whenever we want.

Let’s see an example:

OUTPUT :

In this example, we can see that we can switch back and forth between coroutines whenever we want.

So if we write our own custom scheduler that handles the switching between multiple coroutines, we can achieve with single threading what we do with multithreading.

Coroutines have many applications such as concurrency and other programming patterns can also be implemented, like Producer-Consumer or Sender-Receiver in network programming. I’ll be exploring those in upcoming articles.

Coroutines are also the building blocks of many frameworks such as asyncio, twisted, aiohttp. They can also be chained together to make pipelines and solve problems.

Better Programming

Advice for programmers.

Sign up for The Best of Better Programming

By Better Programming

A weekly newsletter sent every Friday with the best articles we published that week. Code tutorials, advice, career opportunities, and more! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store