Python 中的协程是编程中的基本概念之一,它扩展了传统函数的能力。它们特别适用于异步编程和复杂的数据处理管道。
协程是函数和生成器概念的延伸。它们旨在执行协作式多任务处理和管理异步操作。
传统函数即子程序具有单一的入口点和出口点,而协程可以在多个点暂停和恢复其执行,这使其具有高度灵活性。
协程的关键特性
以下是 Python 中协程的一些关键特性:
-
多个入口点:协程不像传统函数那样受单一入口点的限制。它们可以在遇到
yield
语句时暂停执行并在稍后恢复。这使得协程能够处理涉及等待或处理异步数据的复杂工作流程。
-
无中心协调者:当我们看到传统的函数即子程序时,它们通常由主函数协调,而协程则更独立地运作。它们可以像流水线一样相互交互,数据流经一系列协程,每个协程执行不同的任务。
-
协作式多任务处理:协程支持协作式多任务处理。这意味着程序员而不是操作系统或运行时控制何时协程让出和恢复执行,从而对执行流有更细粒度的控制。
子程序 vs. 协程
子程序是具有单一入口点的传统函数,没有内置机制用于暂停或恢复执行。它们按照定义的顺序调用,并处理带有简单控制流的任务。
协程则是具有多个入口点的高级函数,可以暂停和恢复其执行。它们适用于需要异步执行、复杂控制流和数据管道的任务。它们通过允许程序员控制任务之间何时切换来支持协作式多任务处理。
以下表格有助于理解子程序和协程之间的关键差异和相似之处,使我们更容易理解它们各自在编程中的作用和功能。
标准 |
子程序 |
协程 |
定义 |
执行任务的一系列指令。 |
可以暂停和恢复执行的子程序的泛化形式。 |
入口点 |
单一入口点。 |
多个入口点;可以暂停和恢复执行。 |
执行控制 |
由主函数或其他控制结构调用。 |
可以使用 next() ,send() 和 close() 方法来控制。 |
目的 |
执行特定的任务或计算。 |
管理异步操作、协作式多任务处理和复杂的工作流程。 |
调用机制 |
通常由主函数或其他子程序调用。 |
使用 next() ,send() 和 close() 方法调用和控制。 |
数据处理 |
没有内置的数据交换机制;通常使用参数和返回值。 |
可以使用带有 yield 的 send() 接收和处理数据。 |
状态管理 |
在调用之间没有内在的状态维护机制。 |
在挂起期间维持执行状态,并可以从上次停止的地方恢复。 |
使用 |
用于将代码模块化为易于管理的部分。 |
用于异步编程、管理数据管道和协作式多任务处理。 |
并发 |
不是专门为并发执行设计的;通常用于顺序编程。 |
支持协作式多任务处理,并可以处理异步任务。 |
示例用途 |
辅助函数、实用函数。 |
数据管道、异步任务、协作式多任务处理。 |
控制流 |
执行按照代码中的线性路径进行。 |
执行可以根据 yield 点在协程间来回跳跃。 |
协程的执行
协程使用 __next__()
方法启动,该方法开始协程并推进到第一个 yield
语句。然后协程等待一个值被发送给它。使用 send()
方法向协程发送值,协程可以处理这些值并可能产生结果。
基本协程示例
协程使用 yield
语句,既可以发送也可以接收值。与生成器不同的是,生成器为迭代产生值,而协程通常使用 yield
接收输入并基于该输入执行动作。以下是一个 Python 协程的基本示例。
def print_name(prefix):
print(f"查找前缀: {prefix}")
while True:
name = (yield)
if prefix in name:
print(name)
corou = print_name("欢迎来到")
corou.__next__()
corou.send("tutorialspoint")
corou.send("欢迎来到 tutorialspoint")
输出:
查找前缀: 欢迎来到
欢迎来到 tutorialspoint
关闭协程
由于协程可能会无限期运行,因此在不再需要时正确关闭它们是很重要的。close()
方法终止协程并处理清理。如果我们试图向已关闭的协程发送数据,则会引发 StopIteration
异常。
示例
以下是关闭 Python 中的协程的示例。
def print_name(prefix):
print(f"查找前缀: {prefix}")
try:
while True:
name = (yield)
if prefix in name:
print(name)
except GeneratorExit:
print("关闭协程!!")
corou = print_name("来")
corou.__next__()
corou.send("回来 谢谢你")
corou.send("谢谢你")
corou.close()
输出:
查找前缀: 来
来 回 谢 谢 你
关闭协程!!
为管道链接协程
协程可以串联起来形成处理管道,这样可以让数据流过一系列阶段。这对于分阶段处理数据序列尤其有用,每个阶段执行特定的任务。
示例
下面的示例展示了为管道链接协程。
def producer(sentence, next_coroutine):
'''
将输入句子分割成标记并发送到下一个协程。
'''
tokens = sentence.split(" ")
for token in tokens:
next_coroutine.send(token)
next_coroutine.close()
def pattern_filter(pattern="ing", next_coroutine=None):
'''
根据指定的模式过滤标记并将匹配的标记发送到下一个协程。
'''
print(f"搜索 {pattern}")
try:
while True:
token = (yield)
if pattern in token:
next_coroutine.send(token)
except GeneratorExit:
print("过滤完成!")
next_coroutine.close()
def print_token():
'''
接收标记并打印它们。
'''
print("我是接收端,我将打印标记")
try:
while True:
token = (yield)
print(token)
except GeneratorExit:
print("打印完成!")
pt = print_token()
pt.__next__()
pf = pattern_filter(next_coroutine=pt)
pf.__next__()
sentence = "tutorialspoint 欢迎您来学习并在职业中取得成功!"
producer(sentence, pf)
输出:
我是接收端,我将打印标记
搜索 ing
欢迎您来学习
打印完成!
过滤完成!