程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Explain Python cooperation process thoroughly in one article

編輯:Python

1. introduction

In the last article , We introduced Python Medium yield Keywords and generator functions that depend on their implementation . python Iterators and generators in

The generator function is very close to the coroutine in form , In this article, we will introduce the cooperative process in detail .

2. coroutines

Coroutines are also known as micro threads , Although there is only one thread in the whole execution process , But the execution of a method can be suspended 、 Give up CPU Give me another way , Wait until the appropriate time to return to the original method to continue execution , But there is no calling relationship between the two methods , They are similar to system interrupts or multithreading . thus , We can see the following advantages of cooperative process :

  1. High execution efficiency , By switching in execution , Let multiple methods execute almost simultaneously , Reduce IO wait for , Effectively improve the execution efficiency
  2. Better performance than multithreading , For multithreaded concurrent programming , Switching between multiple threads takes a certain amount of time , The time consumption of CO process switching is very small , And the greater the concurrency, the more obvious the advantage
  3. Programming is relatively simple , Because multiple methods in a coroutine are in the same thread , So there is no competitive condition in the process , There is no need to consider locking

2.1. Example

>>> def simple_coroutine():
... print('-> coroutine started')
... x = yield
... print('-> coroutine received:', x)
...
>>> my_coro = simple_coroutine()
>>> my_coro
<generator object simple_coroutine at 0x100c2be10>
>>> next(my_coro)
-> coroutine started
>>> my_coro.send(42)
-> coroutine received: 42
Traceback (most recent call last):
...
StopIteration

You can see , In the example above ,yield It is no longer the familiar appearance on the left of the formula , Instead, it becomes the right value of the variable assignment , in fact , here yield Values can also appear on the right 、 Variable or expression . When the program is executed to yield When the expression , The collaboration process is suspended , At the same time return to yield Value on the right ( If any ) Execute on this collaboration send The operation is actually to send The parameters of the method are passed to yield The lvalue of an expression , Then the program continues to run .

3. Status of the process

There are four states in a collaboration process :

  1. GEN_CREATED — Wait for execution to begin
  2. GEN_RUNNING — Being implemented
  3. GEN_SUSPENDED — stay yield Pause at expression
  4. GEN_CLOSED — end of execution

By using inspect.getgeneratorstate Function can return one of the above four status strings . Only when a coroutine is in GEN_SUSPENDED State can be called send Method , Otherwise, an exception will be thrown :

>>> my_coro = simple_coroutine()
>>> my_coro.send(1729)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

3.1. Execution of the agreement

>>> def simple_coro2(a):
... print('-> Started: a =', a)
... b = yield a
... print('-> Received: b =', b)
... c = yield a + b
... print('-> Received: c =', c)
...
>>> my_coro2 = simple_coro2(14)
>>> from inspect import getgeneratorstate
>>> getgeneratorstate(my_coro2)
'GEN_CREATED'
>>> next(my_coro2)
-> Started: a = 14
14
>>> getgeneratorstate(my_coro2)
'GEN_SUSPENDED'
>>> my_coro2.send(28)
-> Received: b = 28
42
>>> my_coro2.send(99)
-> Received: c = 99
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> getgeneratorstate(my_coro2)
'GEN_CLOSED'

The following figure shows the execution of the above code :

3.2. Pre excitation

So you need to call... First next Method , Let the coordination process run to the first yield expression , This process is called “ Pre excitation ”(prime) All co processes must be pre excited and then used , This time, next Calls always seem superfluous , Without him, he would make a mistake , So we can add a decorator to our cooperation , To automatically complete the pre excitation function after the collaboration is created .

from functools import wraps
def coroutine(func):
@wraps(func)
def primer(*args,**kwargs):
gen = func(*args,**kwargs)
next(gen)
return gen
return primer

About decorators , You can refer to : python Decorator and its principle in

3.3. close

There are several situations that will make the process enter GEN_CLOSED state :

  1. With the iterator 、 Generator functions , When we keep executing next Method or send Methods make all yield The expressions are executed in turn , Until the last one yield After the expression is executed , Will throw StopIteration abnormal , At this point, the coordination process enters GEN_CLOSED state
  2. The process also provides close Method , No matter what the process is in ,close Method can immediately let the coroutine enter GEN_CLOSED state
  3. If an uncaught exception occurs during the collaboration process , Exceptions are first passed to next or send Method throw , The collaboration process will also end
  4. You can also call throw Method actively passes an exception to the coroutine and throws , The purpose is to make the coroutine throw an exception and close the coroutine , in fact close Method is also by letting the coroutine throw GeneratorExit Exception implemented
  5. There is also a situation that will cause the synergy to enter GEN_CLOSED state , That is, it is recycled when there is no reference to it

About Python Garbage collection mechanism , Reference resources : python Memory management and garbage collection

3.4. Example — Calculate the moving average by using the co process

from collections import namedtuple
Result = namedtuple('Result', 'count average')
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield
if term is None:
break
total += term
count += 1
average = total/count

You can see , In the above example , A coroutine is an infinite loop , As long as the caller keeps sending values to the coroutine , He will keep adding up 、 Calculate the moving average , Until coordinated close Method is called or the collaboration object is garbage collected .

4. It's co-operative return

In the following example , We added a return statement at the end of the above code to calculate the moving average .

from collections import namedtuple
Result = namedtuple('Result', 'count average')
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield
if term is None:
break
total += term
count += 1
average = total/count
return Result(count, average)
>>> coro_avg = averager()
>>> next(coro_avg)
>>> coro_avg.send(10)
>>> coro_avg.send(30)
>>> coro_avg.send(6.5)
>>> coro_avg.send(None)
Traceback (most recent call last):
...
StopIteration: Result(count=3, average=15.5)

You can see , In the end, send to the coordination process None This leads to the withdrawal of the coordination process , Throw the StopIteration This return value is carried in , adopt StopIteration Of value Field we can take the value :

5. Delegation generator — yield from

yield from Statement can simplify... In generator functions yield expression , This has been introduced in our previous article :

>>> def gen():
... for c in 'AB':
... yield c
... for i in range(1, 3):
... yield i
...
>>> list(gen())
['A', 'B', 1, 2]

It can be rewritten as :

>>> def gen():
... yield from 'AB'
... yield from range(1, 3)
...
>>> list(gen())
['A', 'B', 1, 2]

contain yield from The function of the statement is called a delegate generator , He opened a two-way passage , Connect the outermost caller to the innermost child generator , Enables both to send and output values directly , You can also pass in exceptions directly , In the middle of the process in order to add any intermediate processing code . yield from Statement will wait for the child generator to terminate and throw StopIteration abnormal , And the sub generator passes return The value returned by the statement will become yield from Statement .


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved