A brief analysis of the concept of python correlates

  • 2020-07-21 09:02:08
  • OfStack

This article is based on the learning experience of the python coroutine. The following is the full story:

The history of coroutines is a long story, starting with generators.

If you've read my previous article python Adventures: Iterators and generators, you should be familiar with the concept of generators. The generator saves memory and produces results only when used.


#  Generator expression 
a = (x*x for x in range(10))
# next Generate values 
next(a()) #  The output 0
next(a()) #  The output 1
next(a()) #  The output 4

Unlike generators that produce data, coroutines can receive data as they produce it, specifically by placing yield on the right side of the expression. We can use.send () to send data to the coroutine function.


 def writer():
  print('-> coroutine started')
  for i in range(8):
    w = yield
    print(i+w)

w = writer()
#  It's still a generator 
>>> w
<generator object writer at 0x000002595BC57468>
#  The first to use next() Activate the coroutine 
>>> next(w)
-> coroutine started
#  To send data 
>>> w.send(1)
1
# send To the first 8 Then an exception is thrown 
#  Because the coroutine is over 
---------------------------------------------------------------------------
StopIteration               Traceback (most recent call last)

In step 1, you must activate the coroutine function using next() to send data in step 1 using.send ().

As you can see, after receiving the data for the eighth time, an end exception is generated because the program flow has ended, which is normal. Add an exception handler. What if you need to pass data between two coroutines?


def writer():
  while True:
    w = yield
    print('>>', w)

def writer_wrapper(coro):
  #  The activation 
  next(coro)
  while True:
    #  Exception handling 
    try:
      x = yield
      #  Send data to writer
      coro.send(x)
    except StopIteration:
      pass
w = writer()
wrap = writer_wrapper(w)
#  The activation 
next(wrap)
for i in range(4):
  wrap.send(i)
#  The output 
>> 0
>> 1
>> 2
>> 3

In the code above, the data is passed first to writer_wrapper and then to writer.

data - > writer_wrapper - > writer

I could write it this way, but it's a little tricky to preactivate and add exceptions. The emergence of yield from can solve this problem, and also transmit data:


def writer():
  while True:
    w = yield
    print('>>', w)
def writer_wrapper2(coro):
  yield from coro

1 line of code to solve the problem.

In summary, yield from is equivalent to providing a channel for data to flow between coroutines. When yield from coro is used in writer_wrapper2, coro gains control at this time, and writer_wrapper2 is blocked when we use.send () data until writer prints the result.

At this stage, the coroutines are essentially generators.

Even if we use yield from simplifies the process, coroutines and generator of knowledge to understand or mentally, and yield from in asynchronous programming has many bad (asyncio is used yield from), so in the 3.5 version of the python, abandoned the yield from, joined the two new keyword async and await coroutines is no longer the generator type at the same time, but the native coroutines type.

Now let's define a coroutine that looks like this:


async def func():
  await 'some code'

I don't know how to use a coroutine that is not used for asynchrony. So that's the end of the introduction to coroutines. Thank you for your support for this site.


Related articles: