Python 协程

2025年3月17日 | 阅读 7 分钟

在本教程中,我们将学习 Python 中的协程。我们将详细讨论 Python 中的协程、子例程、协程执行和协程关闭。

在我们开始学习协程之前,我们必须对 Python 中的子例程有一个基本的了解。所以,在本教程中,我们将从 Python 子例程开始。

Python 子例程

我们都知道 Python 中的函数,我们可能知道也可能不知道这些 Python 函数也称为过程、子进程或子例程。

通常,Python 中的函数是一系列打包的指令,它们作为一个单元执行以在代码中执行特定任务。当我们把一个复杂的函数逻辑分成几个独立的、像一个独特的函数(嵌套函数等)一样工作的步骤时,那么这些主函数中的辅助函数或嵌套函数就称为 Python 中的子例程。

我们通过主函数调用 Python 中的子例程,主函数负责在使用函数中的子例程时进行协调。Python 中的所有子例程只有一个入口点,即主函数的开始。

主函数

Python Coroutines

看到上面给出的图后,我们可以轻易地得出结论:在使用子例程时,主函数需要协调子例程之间的工作,并且主函数也是 Python 中子例程的唯一入口点。

什么是协程?

现在,在本节中,我们将讨论协程,它基本上是子例程的一种泛化。

协程通常用于进程的协作多任务处理,该进程会自愿地(yield)在一段时间内(周期性地)放弃控制权。当协程空闲时,它也用于允许多个应用程序同时运行。

Python Coroutines

协程与线程不同,在协程中,由程序员和编程语言决定何时切换协程,而在线程的情况下,由操作系统调度线程之间的切换。

协程与子例程的区别

协程子例程
与子例程不同,协程有多个入口点。子例程只有一个入口点,即主函数。
我们只能从一个点挂起和恢复 Python 子例程的执行,并且只能从起点恢复。我们可以从函数中的多个点挂起和恢复 Python 协程的执行,并且我们也可以从上次挂起它的地方恢复执行。
与子例程不同,Python 协程没有主函数来特别地排序和协调它们的执行。在 Python 子例程中,主函数协调多个子例程并控制它们的执行。
协程是协作的,因为当我们在执行它们时,它们共同形成一个管道结构。子例程形成线性执行结构。
在协程中,我们有一个协程来显示输入数据的结果。对于子例程,其中数据的处理结果由主函数显示。

Python 中的协程

协程与 Python 中的生成器非常相似,但协程在 yield(交出)语句上进行了一些修改,并增加了一些额外的方法。Python 协程也能消耗输入数据,而生成器只能为函数中的迭代生成数据。

在 Python 版本高于 2.5 的版本中,我们可以看到 yield 语句在协程中发生了一些小的变化,在此更改之后,yield 语句可以用作表达式。

示例:在右侧赋值 yield,即 LineOfCode = (yield)

我们发送到程序中协程的任何值都将被协程捕获并通过 yield 表达式返回。我们只能通过 send() 函数向协程发送值。

考虑下面程序中的协程,它将只打印前缀中带有“Officer”的名称,我们将使用 send() 函数将名称发送到协程。

示例

输出

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 异常,我们可以通过正常的异常捕获方式来捕获它。

示例 -

输出

Coroutine object searching for the prefix: Officer
Officer Tony Stark
Officer Steve Rogers
Now we have closed the coroutine!!

我们必须记住,如果我们尝试在关闭协程后向协程对象发送值,程序将在输出中引发 StopIteration 异常。

通过链接协程创建管道结构

我们可以使用协程来创建管道结构。在我们将协程链接在一起之后,我们可以使用 push() 方法将给定的数据推送到创建的管道结构中。要在程序中创建具有协程的管道结构,我们需要注意以下几点:

我们必须提供一个初始源,即生产者,它将衍生出完整的管道结构。通常,生产者本身不是协程,它们只是一个简单的方法。

我们必须在管道末端创建一个接收器,接收器将充当管道的终点。接收器是协程管道中的一点,它可能会收集所有输入数据并显示出来。

理解下面带有管道结构的协程程序。

示例

输出

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!!

解释 -

这样,我们就可以使用由多个协程创建的管道来过滤输入数据。过滤后的结果数据将在管道的接收器处显示,正如我们在上面的输出中看到的。