In this tutorial, we are going to learn about coroutine in Python. We will discuss the coroutines, subroutine, execution of coroutines, and closing of coroutine in Python in details.
Before we start learning about coroutine, we must have a basic understanding of subroutines in Python. So, we will start with the Python subroutine in this tutorial.
As we all are known with functions in Python, we may or may not know that these Python functions are also known as a procedure, subprocess, or subroutines.
Generally, a function in Python is a packed sequence of instructions that work as a unit to perform a certain task in the code. When we divide the complex function's logic into several steps that are self-contained and work like a unique function (nested functions etc.), then these helpers or nested functions in the main function are called subroutines in Python.
We call subroutines by the main function in Python that is responsible for the coordination while using subroutines in a function. All subroutines in Python have only a single entry point, i.e., starting of the main function.
After seeing the figure given above, we can easily conclude that the main function is required to coordinate between the subroutines while using them, and the main function is also the only entry point for subroutines in Python.
What are Coroutines?
Now, in this section, we will talk about coroutines that are basically a generalization of subroutines.
Coroutines are generally used for cooperative multitasking of a process that is voluntarily giving away (yield) control over a period of time (periodically). A coroutine is also used when idle to enable the running of multiple applications simultaneously.
Coroutines are different from the thread as in coroutines; it is the programmer and the programming language that decides when coroutines are to be switched, whereas in the case of threads, the operating system schedule the switching between threads.
Difference between Coroutines and Subroutines
Coroutines in Python
Coroutines are very similar to generators present in Python but coroutines come with a few modifications in the yield (give away) statement and some extra methods given in it. Python coroutines also able to consume the input data, whereas the generators were only able to produce data for iteration in function.
In Python having versions higher than 2.5, we can observe a slight change introduced in the yield statement of coroutines, and after this change, the yield statement can be used as an expression.
Example: Assignment of yield on the right side, i.e., LineOfCode = (yield)
Any value that we send to the coroutine in the program will be captured by the coroutine and return only through the yield expression. We can send a value to coroutine only by the send() function.
Consider the given coroutines in the below program that will print out names only that have 'Officer' in their prefix and we will send the names to coroutines using the send() function.
Enter first name: Alex Enter second name: Officer Steve Rogers Enter third name: Officer Natasha Widow Coroutine object searching for the prefix: Officer Officer Steve Rogers Officer Natasha Widow
So, as we can see in the above program that we have taken three names from the user as input data, we have sent the user input data to the coroutine defined in the function by send() method. We have used the "Officer" keyword in coroutine to search the names that have officer prefix in their name, and coroutine will only print these matched names only as we can see in the output.
Execution of Coroutines
In Python, the execution of coroutine is very similar to generators. Nothing will happen when we call the coroutine in the program; it will only run to the following two responses: send() and next() function.
We can clearly observe in the above example; coroutine starts executing only after we called the _next_() method in the program. After the coroutine is called, the execution advances to yield the first expression.
After that, the execution of the coroutine pauses and waits for the value to send to the coroutine object. After the first value is sent to the coroutine object, it checks if the required prefix is present, and if it is, then the object will print the name with the prefix. And after printing the name, it goes through a continuous loop till it encounters, name = (yield) expression again.
To close coroutine, we have to use the close() function in the program. When we close the coroutine, it will produce an exception, i.e., GeneratorExit exception that we can catch in a normal way of catching the exception.
Coroutine object searching for the prefix: Officer Officer Tony Stark Officer Steve Rogers Now we have closed the coroutine!!
We have to remember that if we try to send values to coroutine objects after closing the coroutine, the program will raise a StopIteration exception in the output.
Creating Pipeline Structure by Chaining Coroutine
We can use coroutine to create a pipe structure. After we chain together the coroutine, we can push the given data through the created pipe structure using the push() method. To create a pipe structure in the program with coroutines, we have to take care of the following things:
We have to give an initial source, i.e., producer, that will derive the complete pipeline structure. Generally, producers are not a coroutine in itself, and they are just a simple method.
We have to create a sink at the end of the pipeline, and the sink will act as the endpoint of the pipeline. The sink is the point in the coroutine pipeline, which might collect all the input data and displays it.
Understand the following program of coroutine with pipeline structure.
I'm sink in the pipeline, and I am used to printing the tokens given. In the input sentence, we are searching for words that end with ing running moving We are done with filtering the given input sentence!!
That's how we can use the pipeline created with multiple coroutines and filtering the input data. The filtered or resultant data will be displayed at the sink of the pipeline, as we can see in the above output.