Python - 协程



Python 协程是编程中的一个基本概念,它扩展了传统函数的功能。它们对于异步编程和复杂的数据处理管道特别有用。

协程是函数和生成器概念的扩展。它们旨在执行协作式多任务处理和管理异步操作。

在传统函数中,即具有单个入口和出口点的子例程,而协程可以通过使其高度灵活,在不同点暂停和恢复执行。

协程的主要特性

以下是 python 中协程的主要特征 -

  • 多个入口点:协程不像传统函数那样局限于单个入口点。他们可以在某些时候暂停执行,当他们点击 yield 语句时,稍后继续执行。这允许协程处理涉及等待或处理异步数据的复杂工作流。
  • 无中央协调器:当我们看到传统函数时,即子例程,它们通常由主函数协调,但协程更独立地运行。它们可以以流水线方式相互交互,其中数据流经一系列协程,每个协程执行不同的任务。
  • 协作多任务处理:协程支持协作式多任务处理。这意味着,程序员通过允许对执行流进行更精细的控制,来控制协程何时生成和恢复,而不是依赖操作系统或运行时在任务之间切换。

子例程 VS. 协程

子例程是具有单个入口点的传统函数,没有用于暂停或恢复执行的固有机制。它们按定义的顺序调用,并通过简单的控制流处理任务。

协程是具有多个入口点的高级函数,可以暂停和恢复其执行。它们对于需要异步执行、复杂控制流和数据管道的任务非常有用。它们通过允许程序员控制何时在任务之间切换来支持协作式多任务处理。

下表有助于理解子例程和协程之间的主要区别和相似之处,让我们更容易掌握它们在编程中各自的角色和功能。

标准 子 例 程 协程
定义 执行任务的指令序列。 可以暂停和恢复执行的子例程的泛化。
入场点 单一入口点。 多个入口点;可以暂停和恢复执行。
执行控制 由 main 函数或 control structure 调用。 这些可以暂停执行并在以后恢复,并且程序员可以控制切换。
目的 执行特定任务或计算。 管理异步操作、协作式多任务处理和复杂的工作流程。
调用机制 通常由 main 函数或其他子例程调用。 使用 'next()'、'send()' 和 'close()' 方法调用和控制。
数据处理 没有用于处理数据交换的内置机制;通常使用参数和返回值。 可以使用 'yield' 和 'send()' 接收和处理数据。
状态管理 没有在调用之间维护状态的固有机制。 在暂停之间保持执行状态,并且可以从中断的位置恢复。
用法 这些用于将代码模块化为可管理的块。 这些用于异步编程、管理数据管道和协作式多任务处理。
并发 本质上不是为并发执行而设计的;通常用于顺序编程。 支持协作式多任务处理,并且可以处理异步任务。
示例用法 辅助函数、实用程序函数。 数据管道、异步任务、协作式多任务处理。
控制流 执行遵循代码的线性路径。 执行可以根据 yield point 在协程之间来回跳转。

协程的执行

协程是使用 __next__() 方法启动的,该方法启动协程并将执行推进到第一个 yield 语句。然后,协程会等待向其发送值。send() 方法用于将值发送到协程,然后协程可以处理这些值并可能产生结果。

基本协程示例

协程使用 yield 语句,该语句可以发送和接收值。与生成迭代值的生成器不同,而作为协程,通常使用 yield 来接收输入并根据该输入执行操作。以下是 Python 协程的基本示例 -


def print_name(prefix):
	 	 print(f"Searching prefix: {prefix}")
	 	 while True:
	 	 	 	 name = (yield)
	 	 	 	 if prefix in name:
	 	 	 	 	 	 print(name)

# Instantiate the coroutine
corou = print_name("Welcome to")

# Start the coroutine
corou.__next__()

# Send values to the coroutine
corou.send("qikepu")
corou.send("Welcome to qikepu")

输出

Searching prefix: Welcome to
Welcome to qikepu

关闭协程

协程可以无限期运行,因此在不再需要它们时正确关闭它们非常重要。close() 方法终止协程并处理清理。如果我们尝试将数据发送到已关闭的协程,它将引发 StopIteration 异常。

以下是在 python 中关闭协程的示例 -


def print_name(prefix):
	 	 print(f"Searching prefix: {prefix}")
	 	 try:
	 	 	 	 while True:
	 	 	 	 	 	 name = (yield)
	 	 	 	 	 	 if prefix in name:
	 	 	 	 	 	 	 	 print(name)
	 	 except GeneratorExit:
	 	 	 	 print("Closing coroutine!!")

# Instantiate and start the coroutine
corou = print_name("Come")
corou.__next__()

# Send values to the coroutine
corou.send("Come back Thank You")
corou.send("Thank you")

# Close the coroutine
corou.close()

输出

Searching prefix: Come
Come back Thank You
Closing coroutine!!

链接管道协程

协程可以链接在一起以形成一个处理管道,允许数据流经一系列阶段。这对于在每个阶段执行特定任务的阶段中处理数据序列特别有用。

下面是显示管道链接协程的示例 -


def producer(sentence, next_coroutine):
	 	'''
	 	Splits the input sentence into tokens and sends them to the next coroutine.
	 	'''
	 	tokens = sentence.split(" ")
	 	for token in tokens:
	 	 	 next_coroutine.send(token)
	 	next_coroutine.close()

def pattern_filter(pattern="ing", next_coroutine=None):
	 	'''
	 	Filters tokens based on the specified pattern and sends matching tokens to the next coroutine.
	 	'''
	 	print(f"Searching for {pattern}")
	 	try:
	 	 	 while True:
	 	 	 	 	token = (yield)
	 	 	 	 	if pattern in token:
	 	 	 	 	 	 next_coroutine.send(token)
	 	except GeneratorExit:
	 	 	 print("Done with filtering!!")
	 	 	 next_coroutine.close()

def print_token():
	 	'''
	 	Receives tokens and prints them.
	 	'''
	 	print("I'm the sink, I'll print tokens")
	 	try:
	 	 	 while True:
	 	 	 	 	token = (yield)
	 	 	 	 	print(token)
	 	except GeneratorExit:
	 	 	 print("Done with printing!")

# Setting up the pipeline
pt = print_token()
pt.__next__()

pf = pattern_filter(next_coroutine=pt)
pf.__next__()

sentence = "qikepu is welcoming you to learn and succeed in Career!!!"
producer(sentence, pf)

输出

I'm the sink, I'll print tokens
Searching for ing
welcoming
Done with filtering!!
Done with printing!