You are currently viewing What are Coroutines in Python and How to Create it

What are Coroutines in Python and How to Create it

You must have used the functions in the Python program to perform a certain task. These functions in the Python program are known as subroutines. Subroutines follow a set pattern of execution like entering at one point(subroutine or function call) and exiting at another point(return statement).

Coroutines are similar to subroutines but unlike subroutines, coroutines can enter, exit, and resume at different points during the execution. Coroutines are used for cooperative multitasking which means the control is passed from one task to another to enable multiple tasks to run simultaneously.

Introduction To Coroutines

Coroutines (generator-based coroutines) are a specialized version of generators and like them, they can be paused and resumed using the yield keyword at the time of execution.

Generators generate data, whereas coroutines can do both, generating and consuming data, with a slight difference in how the yield is used within coroutines. We can use yield as an expression (value = yield) within coroutines, which means that yield can both generate and consume values.

To justify the above point, consider the following example in which we created a function that exhibits the behavior of a coroutine.

The above code defines a coroutine function called cor_func, which searches for a parameter char. The function cor_func uses while True to run an infinite loop, and within the loop, yield is encountered, which is a halt in the execution and allows us to send data in the meantime. The caller’s data is saved inside the variable data.

If the character char is present in the data, the function prints the message True, otherwise, the message False is printed.

We created the instance of the coroutine (cor_func("e")) and stored it inside the variable value.

The coroutine is started by calling value.__next__(). The coroutine function will execute until it reaches the yield, allowing us to send data.

We first sent the string "hello" using the value.send("hello") and the string will be checked if the character "e" is present in it, since there is "e" in "hello", the output would be True. The same process will be repeated for value.send("GeekPython") and value.send("Geek") as well.

Closing the Coroutine

The close() method, as the name implies, is used to close the coroutine, which means that no more values can be sent to the coroutine.

Consider the previous code, which we modified by adding the value.close() method.

We called the close() method on the coroutine, which will close the coroutine and prevent it from receiving further values. However, when we tried to send the string "Geek" to the coroutine, we got the following result.

Since the coroutine had already been closed, the send() method that followed the close() method threw the StopIteration exception.

Async Coroutine

We can define a coroutine function (async def) and pause the process until a specific task is completed by using the (async/await) keywords.

The asyncio module was imported, which allows us to write asynchronous code. Then we defined the asynchronous coroutine function (coroutine_func()), which prints a message and then waits one second before printing another message using await asyncio.sleep(1).

We created a coroutine function instance and stored it in the variable cor. When we printed, the coroutine object was returned.

We can use the asyncio.run() function and pass in our coroutine object cor to run the above coroutine.

Concurrency Using Coroutine

We can think of it as the ability to run multiple tasks concurrently in an overlapping manner. Let’s understand with an example.

Three coroutine functions are defined in the preceding code.

The coroutine function read_file() opens the file test.txt, waits for one second, reads the content, and then prints it.

The coroutine function write_file() opens the file in append mode, waits for one second, writes the data, and then appends to the file.

The message() coroutine function prints one message, then waits for one second before printing another.

The coroutine function main() is defined to run those three coroutine functions concurrently using asyncio.gather(message(), write_file(), read_file()).

Inside the if __name__ == "__main__": block, we executed our main() coroutine function using asyncio.run(main()) and we used the time.perf_counter() to measure the execution time.

The code took 1.0 seconds to execute all three coroutine functions which were due to 1 second delay in the coroutines. These coroutine functions were executed simultaneously.

Concurrently running coroutines

Awaiting Coroutine

Coroutines are awaitables (objects that can be used in an await expression), so they can be awaited from other coroutines. Let’s look at the example to get a grasp of it.

When we run the above code with asyncio.run(write_file()), the coroutine function write_file() is called first, and it opens the test.txt file in append mode and appends the data. Then it proceeds until it encounters the await read_file(), which halts the execution of the write_file() coroutine function.

The execution flow then proceeds to the read_file() coroutine function, which reads the contents of the test.txt file.

When the execution flow returns, the content returned by read_file() is printed.

Inside the test.txt file, we can see that the string "Let's get started" has been appended to our existing data.

Conclusion

Coroutines are very helpful in asynchronous programming in which multiple tasks run concurrently. We’ve seen how multiple coroutines are executed concurrently to save time.

Coroutines can enter, exit, and resume at different points during the execution. They are similar to generators but they have additional features such as support for cooperative multitasking, asynchronous programming, and more.


🏆Other articles you might be interested in if you liked this one

How to use assert statements for debugging in Python?

How to manipulate paths using pathlib module in Python?

Different types of inheritances in Python classes.

How to implement __getitem__, __setitem__ and __delitem__ in Python classes?

How to connect PostgreSQL with Python using psycopg2?

How to display images on the frontend using FastAPI?

Public, Protected, and Private access modifiers in Python.


That’s all for now

Keep coding✌✌