Geek Culture
Published in

Geek Culture

Coroutines vs Coroutine Functions in Python

The subtle difference between coroutine and coroutine functions.


There is a common misconception that coroutine functions and coroutine objects are the same. Well! Not exactly. Though they co-exist, there is a teeny tiny difference between both. It is important to understand the difference between them.

What are coroutines?

Simply put, A coroutine is a regular Python function that is prefixed with the keyword async. To get a quick introduction of asyncio library in Python, please refer here. Coroutines created with async def are called native coroutines as they are built into the standard library from python 3.5.

Coroutines functions vs Coroutines:

To understand the difference, let’s write a simple asynchronous function using the asyncio library.

import asyncio, time

async def hello_world():
await asyncio.sleep(1.0)

loop = asyncio.get_event_loop()
task = loop.create_task(hello_world())

The moment we look at this function we say that this is a coroutine. However, this assumption is only partially right. Please note that asynchronous functions are NOT coroutines. They are just asynchronous functions like any other Python function.Period.

Don’t believe it yet? Let’s test it out. We shall try printing the type of hello_world function.

print(type(hello_world)) Output: 
<class 'function'>

I was as surprised as you are. All these days, I was assuming the moment we declare a Python function with async def they become coroutines. Apparently not. If they are not coroutines, then what are they? They are coroutine functions.

Let’s quickly test this using the inspect module in python.

import inspect print(inspect.iscoroutinefunction(hello_world)) Output: 

Well, then what is a coroutine?

A coroutine is created when a coroutine function is executed/evaluated. In other words, when an async def function is called, it will return a coroutine.

import asyncio, time

async def hello_world():
await asyncio.sleep(1.0)

coro = hello_world()


<class 'coroutine'> 

Voila! We have printed the type of coro variable which has the evaluated function hello_world() and the type is coroutine. The inspect module returns true for iscoroutine() and returns False for iscoroutinefunction().

This is exactly how generators work as well. Just like coroutines and coroutine functions we have generators and generator functions.

def get_number():
values = range(100)

for i in values:
if i % 2 == 0:
yield i

nums_ = get_number()



<class 'function'> 
<class 'generator'>


  • Coroutines and coroutine functions are not the same.
  • A coroutine is what we get by evaluating a coroutine function
  • A function prefixed with async def is a coroutine function
  • A function with a yield keyword is a generator function
  • A generator function evaluates to a generator.

Originally published at on February 10, 2022.

More content at Sign up for our free weekly newsletter. Get exclusive access to writing opportunities and advice in our community Discord.




A new tech publication by Start it up (

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
Dinesh Kumar K B

Dinesh Kumar K B

Python Back-End Developer, AWS | Django | Flask | Azure | |

More from Medium

How to avoid file name collisions in pytest

How to Avoid Issues When Waiting On Multiple Events in Python Asyncio

Cover image for the article showing an abstract metallic design.

Python Pytest Behaviour Driven Framework — pytest-bdd

Understanding how the Python garbage collector works