Hi! I am currently looking for some freelance projects. If you have a project idea in mind please feel free to send me an email or check out my personal website. If you want to get to know about my journey, you can read this article: How I got into programming

Coroutines are similar to generators with a few differences. The main
differences are:

generators are data producers

coroutines are data consumers

First of all let’s review the generator creation process. We can make
generators like this:

deffib():a,b=0,1whileTrue:yieldaa,b=b,a+b

We then commonly use it in a for loop like this:

foriinfib():print(i)

It is fast and does not put a lot of pressure on memory because it
generates the values on the fly rather than storing them in a list.
Now, if we use yield in the above example, more generally, we get a
coroutine. Coroutines consume values which are sent to it. A very basic
example would be a grep alternative in Python:

Wait! What does yield return? Well we have turned it into a
coroutine. It does not contain any value initially, instead we supply it
values externally. We supply values by using the .send() method.
Here is an example:

The sent values are accessed by yield. Why did we run next()? It is
required in order to start the coroutine. Just like generators, coroutines do not
start the function immediately. Instead they run it in response to the
__next__() and .send() methods. Therefore, you have to run
next() so that the execution advances to the yield expression.