Python协程
在本教程中,我们将学习Python中的协程。我们将详细讨论协程、子程序、协程的执行以及协程的关闭。
在开始学习协程之前,我们必须对Python中的子程序有一个基本的了解。因此,在本教程中我们将从Python子程序开始。
Python子程序
我们都知道Python中的函数,但可能并不知道这些Python函数也被称为过程、子进程或子程序。
一般来说,Python中的函数是一系列打包在一起的指令,作为一个单元在代码中执行特定任务。当我们将复杂函数的逻辑分解成若干个自包含且像唯一函数那样工作的步骤(嵌套函数等)时,主函数中的这些辅助或嵌套函数被称为Python中的子程序。
我们通过主函数在Python中调用子程序,主函数负责在函数中使用子程序时的协调工作。在Python中,所有子程序仅有一个入口点,即主函数的开始。
主函数
在看到上面给出的图后,我们可以很容易地得出结论,主函数需要在使用子程序时协调它们之间的关系,而且主函数也是Python中子程序的唯一入口点。
什么是协程
现在,在这节中,我们将讨论协程,它们基本上是子程序的一种推广。
协程通常用于进程的协作多任务处理,它主动地在一段时间内放弃(yield)控制权。在空闲时,协程还可以同时运行多个应用程序。
协程与线程不同,协程的切换由程序员和编程语言决定,而线程的切换由操作系统进行调度。
协程与子程序之间的区别
协程 | 子程序 |
---|---|
不同于子程序,协程有多个入口点。 | 子程序只有一个入口点,即主函数。 |
我们只能从一个点暂停和恢复执行Python子程序,并且只能从起始点继续执行。 | 我们可以从Python协程的多个点暂停和恢复执行,并且可以从最后一次暂停的地方继续执行。 |
不同于子程序,Python协程没有任何主函数来排序和协调它们的执行。 | 在Python子程序中,主函数在多个子程序之间协调并控制它们的执行。 |
协程是协作的,因为它们在执行时形成一个管道结构。 | 子程序形成线性执行结构。 |
在协程中,我们有一个用于显示输入数据结果的协程。 | 对于子程序,给定的数据处理结果由主函数显示。 |
Python中的协程
协程非常类似于Python中的生成器,但协程在yield(放弃)语句和其中的一些额外方法上有一些修改。Python的协程还能够消耗输入数据,而生成器只能在函数中产生数据进行迭代。
在Python的版本高于2.5的情况下,我们可以观察到在协程的yield语句中引入了一个小的变化,并且在此变化之后,yield语句可以用作表达式。
示例: 将yield赋值在右边,即, LineOfCode = (yield)
在程序中,我们发送到协程的任何值都将被协程捕获,并且仅通过yield表达式返回。我们只能通过send()函数向协程发送值。
考虑以下程序中给定的协程,在其中只打印以’Officer’作为前缀的名称,并使用 send() 函数将名称发送给协程。
示例:
# Default function to search prefix
def print_name(prfx):
print("Coroutine object searching for the prefix: {}".format(prfx)) # Searching for prefix
while True:
GivenName = (yield)
if prfx in GivenName: # If required prefix match
print(GivenName) # Print given name
CorouteObject = print_name("Officer") # Taking prefix = Officer for coroutine object
# Taking names as input from user
Name1 = input("Enter first name: ")
Name2 = input("Enter second name: ")
Name3 = input("Enter third name: ")
CorouteObject.__next__() # using _next_() method to call coroutine
# sending input data to coroutine with send() method
CorouteObject.send(Name1)
CorouteObject.send(Name2)
CorouteObject.send(Name3)
输出:
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
解释 –
所以,从上面的程序中可以看到,我们已经从用户那里获取了三个名字作为输入数据,我们通过 send() 方法将用户输入数据发送到了函数中定义的协程中。我们在协程中使用了”Officer”关键字来搜索那些名字中有”officer”前缀的名字,并且协程只会打印这些匹配的名字,正如我们在输出中所看到的。
协程的执行
在Python中,协程的执行与生成器非常相似。当我们在程序中调用协程时,不会发生任何行为;它只会运行到以下两种响应之一:send()和next()函数。
我们可以在上面的示例中清楚地观察到,在程序中调用next()方法之后,协程才开始执行。在协程被调用之后,执行进展到yield表达式。
在那之后,协程的执行暂停并等待要发送给协程对象的值。在第一个值发送到协程对象后,它会检查是否存在所需的前缀,如果存在,则对象将打印带有前缀的名字。在打印完名字之后,它会进入一个持续的循环,直到再次遇到name = (yield)表达式。
关闭协程
要关闭协程,我们必须在程序中使用 close() 函数。当我们关闭协程时,它会产生一个异常,即GeneratorExit异常,我们可以以正常的异常捕获方式来捕获它。
示例
# Default function to search prefix
def print_name(prfx):
print("Coroutine object searching for the prefix: {}".format(prfx)) # Searching for prefix
# Using excption handling by try and except
try:
while True:
GivenName = (yield)
if prfx in GivenName: # If required prefix match
print(GivenName) # Print given name
except GeneratorExit: # Handling GeneratorExit exception
print("Now we have closed the coroutine!!")
CorouteObject = print_name("Officer") # Taking preifx = Officer for coroutine object
CorouteObject.__next__() # using _next_() method
# sending input data to coroutine with send() method
CorouteObject.send("Alexa")
CorouteObject.send("Officer Tony Stark")
CorouteObject.send("Officer Steve Rogers")
# closing the coroutine
CorouteObject.close()
输出:
Coroutine object searching for the prefix: Officer
Officer Tony Stark
Officer Steve Rogers
Now we have closed the coroutine!!
我们必须记住,如果在关闭协程后尝试向协程对象发送值,程序将在输出中引发StopIteration异常。
通过链接协程创建管道结构
我们可以使用协程来创建一个管道结构。在将协程链接在一起之后,我们可以使用push()方法将给定数据通过创建的管道结构推送。在使用协程创建程序中的管道结构时,我们必须注意以下几点:
我们必须提供一个初始来源,即生产者,它将得到完整的管道结构。通常,生产者本身不是协程,而只是一个简单的方法。
我们必须在管道的末端创建一个汇聚点,汇聚点将充当管道的终点。汇聚点是协程管道中的一个点,可能会收集所有输入数据并显示出来。
了解以下带有管道结构的协程程序。
示例:
# Defining producer for the pipeline
def producer(GivenSentence, NextCoroutine):
tokens = GivenSentence.split(" ") # splitting sentence
for token in tokens: # iterating over tokens
NextCoroutine.send(token)
NextCoroutine.close() # closing coroutine
# Defining pattern filter for the pipeline structure
def pattern_filter(SearchPattern = "ing", NextCoroutine = None):
print("In the input sentence, we are searching for words that end with{}".format(SearchPattern)) # Searching for pattern
try:
while True:
token = (yield) # yielding tokens
if SearchPattern in token:
NextCoroutine.send(token) # Sending tokens
except GeneratorExit: # Exception handling for GeneratorExit exception
print("We are done with filtering the given input sentence!!")
# Defining sink for the pipeline
def print_token():
print("I'm sink in the pipeline and I am used to print the tokens given.")
try:
while True:
token = (yield) # yielding tokens
print(token) # printing the tokens from sink
except GeneratorExit:
print("Now we are done with printing!")
# Taking sink variable
PrintToken = print_token()
PrintToken.__next__() # calling sink
# Taking Pattern filter variable
PatternFilter = pattern_filter(NextCoroutine = PrintToken)
PatternFilter.__next__() # calling pattern filter
# Taking a sentence for the producer in the pipeline
GivenSentence = "Steve rogers is running very fast to chase down a train moving with high speed"
producer(GivenSentence, PatternFilter) # calling producer
输出:
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!!
解释 –
这就是我们如何使用由多个协程创建的流水线并筛选输入数据的方式。过滤后或得到的数据将显示在流水线的终点,如上面的输出所示。