1 Star 0 Fork 0

小凡 / python高级文档

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
生成器和协程.md 29.52 KB
一键复制 编辑 原始数据 按行查看 历史
xiaofan 提交于 2023-12-03 10:54 . 文档完善

img

可迭代对象与迭代器

一、绪论

​ 事实上,Python、C++、JAVA 等语言中都有 迭代器 的概念,用法相近,均旨在解决 “如何在复杂场景下尽可能简便地获取数据” 的问题。迭代器的含义类似于遍历链表时所用到的 cur 指针 —— 总是指向当前位置并知道下一个位置 next。

*封面图片* 较好地展现了 *Python* *可迭代对象 (iterable)、迭代器 (iterator)、生成器 (generator) 等概念的基本联系*。为突出其重要性以及便于查阅,特置于醒目的位置。(来自网络)


二、可迭代对象 (iterable)

*可迭代对象 (iterable)* 是一种 能逐一返回其成员项 的对象,常包括:

  • 所有内置序列对象 (字符串、列表、元组)
  • 部分内置非序列对象 (字典、集合、文件对象等)
  • 其他支持迭代协议的对象 (定义有 iter() 方法)
  • 其他支持序列协议的对象 (定义有 getitem() 方法)

​ 既可用内置函数 hasattr() 检查一个对象是否支持迭代协议 (定义有 iter() 方法),以判断其是否为可迭代对象:

# 可见以下常见的内置类型均支持迭代协议 —— 为可迭代对象>>> hasattr([], "__iter__"), hasattr({}, "__iter__"), hasattr('', "__iter__"), hasattr(set(), "__iter__"), hasattr((), "__iter__")(True, True, True, True, True)

​ 也可用内置函数 isinstance() 并结合 collections 模块中的抽象基类 (ABC),以判断一个对象是否为可迭代对象:

>>> from collections import Iterable# 测试常见数据类型的对象, 可见均为可迭代对象(iterable)>>> isinstance([], Iterable), isinstance({}, Iterable), isinstance('', Iterable), isinstance(set(), Iterable), isinstance((), Iterable)(True, True, True, True, True)

​ 可迭代对象可用于 *for 循环* 及各种 *以 iterable 为形参的函数/方法* 中 (如 zip()、map()、enumerate() 等),例如:

>>> help(map)
Help on class map in module builtins:
 
class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.

​ 由此可知,map 派生自 object,其形参包含了可迭代对象 iterables。不仅如此,map 内部定义了 iter() 方法 (支持迭代协议),因此 map 对象也是可迭代对象。而 zip()、enumerate() 等函数亦复如是。

可迭代对象 作为 实参 传递给 内置函数 iter() 时,将 返回一个该可迭代对象的迭代器 (irerator)。这种迭代器适用于对 值/数据成员/元素 的集合的 一次性遍历。使用可迭代对象时,通常无需自行调用 iter() 或自行处理迭代器对象,for 语句会在内部处理好这些操作 —— 创建一个临时量 (临时的未命名变量) 用于在循环/迭代/遍历期间保存迭代器

​ 关于 for 语句,简要回顾如下:

  • for 语句用于对序列 (str、list、tuple) 等 可迭代对象中的元素执行顺序迭代
  • for 语句在迭代前,先 创建一个该可迭代对象的迭代器 (irerator),并用一个临时量 (临时的未命名变量) 保存该迭代器迭代过程顺序遍历和操作迭代器的元素。当 迭代器元素耗尽 时 (包括序列为空 或 迭代器引发 StopIteration 异常等),执行 else 语句 (如果有),然后结束 for 循环 (每次 for 循环都会有一个 内部计数器 用于跟踪下一个元素,每次迭代都会使计数器递增,当计数器值增至迭代器元素总数时循环就会终止)。
  • 若 for 语句中执行了 break,则直接跳过 (for 循环的) else 语句并结束 for 循环。
  • 若 for 语句中执行了 continue,则跳过本次迭代,直接进行下一次。
  • for 语句的内部机制 确保不因迭代器元素耗尽引发 StopIteration 异常 (例如使用 try-except 语句 详见下节)。

​ 例如,经常使用 for 语句对 range() 函数对象迭代,而 range() 函数对象就是一个可迭代对象 (但不是迭代器)

>>> for i in range(1):
	    pass
 
>>> isinstance(range(1), Iterable), isinstance(range(1), Iterator)
(True, False)

​ 注意,上述提到了将要进一步说明的概念 —— 迭代器 (irerator)


三、迭代器 (iterator)

3.1 介绍

迭代器 (iterator) 是一种用于表示 一连串数据流 的对象。

​ 迭代器对象要求支持 迭代器协议 —— 对象须同时支持/实现 iter() 方法和 next() 方法。协议规定:

  • *iterator.iter()* 方法 返回迭代器对象本身 (对可迭代对象使用内置函数 iter() 将返回迭代器),这是同时允许容器和迭代器配合 for .. in ... 语句使用所必须的。
  • iterator.next() 方法 返回容器的下一项元素。重复调用迭代器的 next() 方法 (或用内置函数 next()) 将依次返回数据流中的元素,并在元素耗尽 (无数据项/元素) 时引发 StopIteration 异常。迭代器对象中的数据项/元素耗尽后,再次调用 next() 或 next() 只会再次引发 StopIteration 异常。

​ 迭代器必须具有 iter() 方法用来返回该迭代器对象自身,因此 迭代器必为可迭代对象 (但可迭代对象不一定是迭代器)。换言之,只有迭代器有 next() 方法,而可迭代对象没有

img

迭代器可用于其他可迭代对象适用的大部分场合 (比如 map 同时实现了 iter() 和 next() 方法,故其亦为迭代器)。

>>> from collections import Iterable, Iterator  # 导入两个抽象基类, 可用于检查对象类型
 
>>> lst = [0, 1, 2]  # 创建示例列表 lst
>>> isinstance(lst, Iterable), isinstance(lst, Iterator)  # lst 是可迭代对象, 但还不是迭代器
(True, False)
 
 
>>> iterator = iter(lst)  # 返回可迭代对象 lst 的迭代器, 并由变量 iterator 指向/保存该迭代器
>>> isinstance(iterator, Iterable), isinstance(iterator, Iterator)  # iterator 既是可迭代对象, 也是迭代器
(True, True)
>>> type(iterator)  # 更具体地, 这是一个 “列表迭代器 (list_iterator)”  
<class 'list_iterator'>
 
 
>>> next(iterator)  # 使用内置函数 next(), 输出一个元素
0
>>> iterator.__next__()  # 等价于使用成员方法 __next__(), 输出一个元素
1
>>> next(iterator)  # next() 和 __next__() 效果完全相同
2
>>> next(iterator)  # 迭代器元素耗尽, 调用 next() 引发 StopIteration 异常
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    next(iterator)
StopIteration
>>> next(iterator)  # 迭代器元素耗尽, 再次调用 next() 仍会引发 StopIteration 异常
Traceback (most recent call last):
  File "<pyshell#33>", line 1, in <module>
    next(iterator)
StopIteration

​ 综上可知,迭代器表示一个 数据流,可被 next() 函数不断调用,从首项开始不断返回下一项数据/元素 (而不能回退) 直至耗尽,并抛出 StopIteration。

​ 事实上,不断调用 next() 函数,内部指针随之后移并依次访问各元素;当遍历尽所有元素时,意味着指针移到了最后一个元素后,故不论怎么调用 next() 函数,都无法访问到元素,于是引发 StopIteration 异常;若想要从头访问,须再次载入迭代器对象。

​ 而该 数据流 可视为一个 未知长度的 “有序序列” (比喻),只能通过不断调用 next() 按需计算下一项数据/元素。因此,迭代器是 惰性的,需要返回下一个项时才会计算 (形同赶牛,抽一下动一下)。

​ 事实上,若有必要,迭代器甚至可表示为一个 无限大的数据流,例如全体自然数。而有限长度的序列/非序列等是无法做到存储全体自然数的。

​ 由此可知,for 循环的本质即**:对可迭代对象调用 iter() 返回一个关于该对象的迭代器,然后不断调用 next() 迭代/遍历元素**。例如:

>>> for i in [0, 1, 2]:
	print(i)
 
0
1
2
 
# --------------------------------------- 等价于 ----------------------------------------
 
>>> iterator = iter([0, 1, 2])  # 将从可迭代对象(list)返回一个迭代器
>>> while True:
	    try:                    # 使用 try-except 语句捕获异常, 避免抛出 StopIteration 异常并正常结束循环
		    j = next(iterator)  # 返回迭代器的下一个元素
		    print(j)
	    except StopIteration:   # 当元素耗尽时, 捕获 StopIteration 异常并退出循环/迭代
		    break  
 
0
1
2

​ 这就是为什么我们 从未见过 for 语句引发 StopIteration 异常 —— 它可以使用 try-except 语句 或其他相关代码实现 异常捕获、处理与自动结束

​ 此外,不建议对 可变对象 边迭代边修改,因为如此将可能带来各种 麻烦和困扰


3.2 文件迭代器

​ 设 test.txt 文件的内容为:

First LineLast Line

​ 通常使用 read() / readline() / readlines() 等方式读取文件内容,例如:

>>> fin = open('test.txt')
>>> fin.readline()
'First Line\n'
>>> fin.readline()
'Last Line'
>>> fin.readline()  # fin.readline() 若返回一个空字符串, 说明已经已经读取到最后一行
''
>>> fin.close()

​ 当然,用 for 循环更便捷:

>>> fin = open('test.txt')
>>> for line in fin:
	    print(line, end='')  # print() 函数默认结尾是换行符 \n , 这里手动改成 ''
 
First Line
Last Line
>>> fin.close()

​ 其实,还可以使用 文件迭代器,例如:

>>> fin = open('test.txt')  # 文件对象
>>> fin.__next__()
'First Line\n'
>>> fin.__next__()
'Last Line'
>>> fin.__next__()
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    fin.__next__()
StopIteration
>>> fin.close()

​ 可见,还可使用 next() 方法 (当然 next() 亦可) 逐行读取文件内容,这说明 文件对象是迭代器

>>> from collections import Iterable, Iterator  # 抽象基类
>>> fin = open('test.txt')
>>> isinstance(fin, Iterable), isinstance(fin, Iterator)  # 文件对象 fin 也是迭代器
(True, True)

​ 正如 3.1 节结尾所述:for 循环本质就是对可迭代对象调用 iter() 返回一个迭代器、然后不断调用 next() 迭代/遍历元素


3.3 自定义类实现迭代器

​ 除了 Python 内置迭代器对象,更主要的用法还是 自定义迭代器 —— 自定义类并在其成员方法中同时实现 iter() 方法和 next() 方法 (以支持迭代器协议) 创建迭代器。例如:

>>> class Counter:
    ''' 自定义一个示例迭代器类 (简化到甚至构造方法 __init__() 都不实现) '''
	def __iter__(self):  # __iter__() 方法用于初始化并返回迭代器
		self.num = 0
		return self      # 只需返回自身
	def __next__(self):
		i = self.num
		self.num += 1
		return i
 
 
>>> counter = Counter()  # 实例化一个类对象
>>> isinstance(counter, Iterable), isinstance(counter, Iterator)  # 支持迭代器协议, 是迭代器
(True, True)
 
 
>>> count_iter = iter(counter)  # 返回一个迭代器
>>> isinstance(count_iter, Iterable), isinstance(count_iter, Iterator)  # 迭代器
(True, True)
 
 
>>> next(count_iter)  # 理论上可以无限迭代所有的自然数
0
>>> next(count_iter)
1
>>> next(count_iter)
2

StopIteration 异常 用于 标识迭代的完成防止出现无限循环 的情况,在 next() 方法中可以自行设置在完成指定循环次数后触发 StopIteration 异常以结束迭代:

>>> class Counter2:
	def __iter__(self):
		self.num = 0
		return self
	def __next__(self):
		if self.num <= 3:  # 自定义上迭代上限/停止迭代条件
			i = self.num
			self.num += 1
			return i
		else:
			raise StopIteration  # 主动抛出异常
 
>>> counter2 = Counter2()
>>> count_iter2 = iter(counter2)
>>> next(count_iter2)
0
>>> next(count_iter2)
1
>>> next(count_iter2)
2
>>> next(count_iter2)
3
>>> next(count_iter2)
Traceback (most recent call last):
  File "<pyshell#84>", line 1, in <module>
    next(count_iter2)
  File "<pyshell#77>", line 11, in __next__
    raise StopIteration
StopIteration

​ 当然,最好还是 初始化构造函数 使之 更灵活

>>> class Counter3:
	def __init__(self, start, end): 
		self.start = start
		self.end = end
	def __iter__(self):
		self.indicator = self.start 
		return self  
	def __next__(self):
		if self.indicator <= self.end:
			i = self.indicator
			self.indicator += 1
			return i
		else:
			raise StopIteration
 
>>> counter3 = Counter3(0, 2)  # 实例化类对象时, 自定义传入计数初始值和结束值
>>> count_iter3 = iter(counter3)
>>> next(count_iter3)
0
>>> next(count_iter3)
1
>>> next(count_iter3)
2
>>> next(count_iter3)
Traceback (most recent call last):
  File "<pyshell#93>", line 1, in <module>
    next(count_iter3)
  File "<pyshell#86>", line 14, in __next__
    raise StopIteration
StopIteration

​ 此外,迭代器同样具有 缺点,例如:

  • 迭代器只能不断往下一项元素迭代,而不能回退
  • 迭代器不适用于多线程环境下的可变集合等

四、相关内置函数

4.1 返回迭代器对象 —— iter()

内置函数 iter() 通过可迭代对象 *返回一个关于可迭代对象的迭代器对象*,其函数原型为:

iter(object[, sentinel])

​ 其中,第一个参数 object 必须是 支持迭代协议 (有 iter() 方法) 或 支持序列协议 (有 getitem() 方法,且数字参数从 0 开始) 的集合对象。若不支持这些协议,会引发 TypeError。只传入第一个参数 object 并返回一个迭代器的用法十分常见,上文例子很多不再赘述。

​ 注意,若还传入第二个参数 sentinel,则参数 object 必须是一个 可调用 (callable) 的对象 (如函数)。这种情况下生成的迭代器对象,每次调用其 next() 方法时,都会不带实参地调用 object。若返回的结果是 sentinel 则触发 StopIteration,否则返回调用结果。例如:

>>> callable(list)  # 使用内置 callable() 函数判断是否为可调用对象 (callable object)
True
# ------------------------------------------------------------------------------
>>> reader1 = iter(list, '')  # 参数 sentinel='', 迭代器只有返回 '' 时才会停止
>>> next(reader)
[]
>>> next(reader)
[]
# ------------------------------------------------------------------------------
>>> reader2 = iter(list, [])  # 参数 sentinel='', 迭代器只有返回 [] 时才会停止
>>> next(reader2)
Traceback (most recent call last):
  File "<pyshell#128>", line 1, in <module>
    next(reader2)
StopIteration

​ 可见,使用内置 callable() 函数可判断对象是否可调用。而若类实现了 *callable()* 方法,则其实例是可调用的。

​ 当然,上例仅是说明语法,而非正常用法。有一个经典用法是:读取文件的行直至到达某一特定行为止。下例读取一个文件,直到 readline() 方法返回一个空字符串:

>>> with open('test.txt') as file:  # with 语句下的上下文管理器
	for line in iter(file.readline, ''):  # 逐行读取并迭代
		process_line(line)  # 处理该行的统称/笼统表示方式

4.2 调用下一个元素 —— next()

内置函数 next() 通过 调用迭代器的 next() 方法返回下一项元素,其函数原型为:

next(iterator[, default])

​ 其中,第一个参数 iterator迭代器对象。第二个参数 default 可选,用于设置 在没有下一项元素时返回的默认值/缺省值,以避免抛出异常中断程序。若不设置参数 default,且调用 next() 时无下一项元素则会触发 StopIteration 异常。例如:

>>> list_iter1 = iter([0, 1, 2])
>>> next(list_iter1)
0
>>> next(list_iter1)
1
>>> next(list_iter1)
2
>>> next(list_iter1)  # 不指定 default 且迭代器元素耗尽, 将引发 StopIteration 异常
Traceback (most recent call last):
  File "<pyshell#117>", line 1, in <module>
    next(list_iter1)
StopIteration
>>> next(list_iter1, 'stop')  # 指定 default, 则迭代器元素耗尽, 将输出 default
'stop'

​ 上例不仅说明了 迭代器元素耗尽时抛出 StopIteration 异常的原因,还给出了 next() 函数的另一种用法来避免引发异常。

生成器和协程

一、概述

  • 生成器是一种在 Python 中的迭代器生成器。生成器是一个函数,它生成一个迭代器。当生成器函数被调用时,它不会立即执行,而是返回一个生成器对象,该对象可以被用于迭代。生成器可以利用 yield 语句在函数内部生成值,并在函数调用者处接收这些值。
  • 协程是一种高效的、内存友好的、线程内的并发技术,它可以让您在单个线程内并发地执行多个任务协程是通过使用 async 关键字实现的,并可以在 Python 中的 asyncio 库中使用。与线程不同,协程不需要额外的系统线程,因此它们比线程更高效、更灵活。

在简单的说法,生成器用于生成一系列的值,而协程用于在单个线程中并发执行多个任务。

二、生成器

生成器表达式本质上就是一个迭代器,是定义迭代器的一种方式,是允许自定义逻辑的迭代器。生成器使用generator表示。

  • 生成器可以使用 for 循环或 next() 函数来遍历。当生成器对象被创建时,它会保存函数的当前状态,并在每次调用 next() 或 for 循环时从当前状态开始执行,直到遇到 yield 语句为止。
  • 生成器遇到 yield 语句时,它会生成当前的值,并保存函数的当前状态,以便下次调用时可以从该状态开始继续执行。当生成器再次被调用时,它会继续执行从上次暂停的地方开始,直到遇到下一个 yield 或者 return 语句,或者函数结束为止。

下面是一个生成器函数的示例:

def my_generator():
    for i in range(3):
        yield i

gen = my_generator()
for i in gen:
    print(i)

输出:

0
1
2
123

从上面的示例可以看出,生成器的工作原理是通过保存函数的当前状态,以便每次调用时从当前状态开始继续执行,并使用 yield 语句生成值的。

1)生成器和迭代器的区别

生成器和迭代器是 Python 中的两个相关的概念,但是有一些区别:

  • 定义生成器是一种特殊的迭代器,它可以生成一系列的值,而迭代器是一个对象,它实现了 iternext 方法,可以返回一个值的序列。
  • 创建:生成器可以通过定义生成器函数,在函数内部使用 yield 语句生成值;迭代器可以通过定义迭代器类,在类中实现 iternext 方法。
  • 效率:生成器函数在生成值时只需要暂停函数的执行,因此它具有更高的效率;迭代器类需要维护一个对象状态,因此效率较低。
  • 用途:生成器适用于生成大量的数据,因为它可以在生成数据时保存函数的状态,从而避免占用大量内存;迭代器适用于处理少量数据,因为它需要创建一个对象维护状态。

因此,在实际开发中,我们可以根据数据量和处理效率的需求来选择使用生成器或迭代器。

2)生成器创建方式

在 Python 中,可以通过以下两种方式创建生成器:

1、通过生成器函数创建

通过在函数中使用 yield 语句,可以将函数变为生成器函数,每次调用生成器函数时,可以生成一个生成器。

例如:

def generator_example():
    yield 1
    yield 2
    yield 3

gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))
2、通过生成器表达式创建

生成器表达式是一种简写的生成器创建方式,它基于列表推导式的语法。

例如:

gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))

以上是生成器的两种创建方式,您可以根据实际需求选择使用。

3)生成器表达式

生成器表达式是一种简写的生成器创建方式,它基于列表推导式的语法。

例如:

gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))

在上面的例子中,我们使用生成器表达式创建了一个生成器,该生成器生成从 0 到 2 的整数。然后,我们使用 next() 函数逐个迭代生成器中的值。

4)yield关键字

yield 关键字是 Python 中的一个关键字,用于生成器函数中。它允许一个函数在生成值时暂停其执行,以便在稍后恢复其执行并生成下一个值。这使生成器函数成为一种特殊的函数,可以按需生成一系列值。

5)生成器函数

生成器函数是 Python 中特殊的函数,该函数可生成一个生成器。与普通函数不同,生成器函数可以在每次被调用时生成一个生成器,并在生成器中生成一系列值。

生成器函数通过使用 yield 语句创建生成器。每当函数执行到 yield 语句时,生成器函数的执行就会暂停,并返回 yield 语句后面的值。当再次调用生成器函数时,它将从上次暂停的位置继续执行,直到遇到下一个 yield 语句,或者函数返回。

例如:

def generator_example():
    yield 1
    yield 2
    yield 3

gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))

在上面的例子中,我们定义了一个生成器函数 generator_example,该函数通过使用 yield 语句生成了三个整数:1、2 和 3。然后,我们通过调用该函数并将其结果分配给生成器 gen 来创建生成器,并使用 next() 函数逐个迭代生成器中的值。

6)return 和 yield 异同

returnyield 都是用于在函数中终止执行的关键字,但是它们的作用是不同的。

  • return:当函数调用 return 时,函数立即终止执行,并返回一个值(如果存在)给调用者。该值通常表示函数的最终结果。
  • yield:当生成器函数调用 yield 时,它仅暂停其执行并生成一个值,但不终止函数。在下一次调用该生成器时,它将恢复其执行,直到遇到下一个 yield 或终止函数。

因此,yield 是生成器函数的一个关键字,可以使生成器生成一系列值,而 return 是一般函数的一个关键字,它返回一个值并终止函数

7)yield的使用方法

yield 关键字用于生成器函数。在生成器函数中,我们可以使用 yield 关键字生成一系列值,而无需暂停整个函数。

例如,以下是使用 yield 关键字的简单生成器函数的例子:

def simple_generator():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5

gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
print(next(gen)) # Output: 4
print(next(gen)) # Output: 5

我们可以使用 for 循环或 next() 函数迭代生成器中的值,如下所示:

for value in simple_generator():
    print(value)

# Output:
# 1
# 2
# 3
# 4
# 5

【注意】生成器函数只能迭代一次,所以请确保在使用生成器函数时存储其返回值。

8)for与next

在使用生成器时,我们可以使用两种不同的方法来迭代生成器中的值:for 循环next() 函数。

  • for 循环:通过使用 for 循环,我们可以在生成器中迭代所有值。例如:
def simple_generator():
    yield 1
    yield 2
    yield 3

for value in simple_generator():
    print(value)

# Output:
# 1
# 2
# 3
  • next() 函数:通过使用 next() 函数,我们可以手动控制生成器的迭代。例如:
def simple_generator():
    yield 1
    yield 2
    yield 3

gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3

【注意】在生成器迭代完所有的值后,再使用 next() 函数将导致抛出 StopIteration 异常。因此,在使用 next() 函数时,请确保捕获该异常。

9)send的使用

send() 方法是生成器的一种方法,它允许我们在生成器函数内部向生成器发送数据。该方法允许生成器接收外部数据,并使用这些数据生成结果

在使用 send() 方法时,我们需要在生成器函数中使用 yield 表达式来接收数据,如下所示:

def simple_generator():
    result = yield
    print("Received data:", result)

gen = simple_generator()
next(gen)
gen.send("Hello, World!")
# Output: Received data: Hello, World!

【注意】第一次使用 send() 方法之前,我们需要调用 next() 函数,以启动生成器函数。此外,使用 send() 方法将导致抛出 StopIteration 异常,因此请确保在使用该方法时进行异常处理。

三、协程进阶

协程(Coroutine)是一种编程技巧,用于在多任务环境中实现轻量级的任务切换。与线程不同,协程不需要创建新的系统级线程,并且消耗的资源也比线程更少。因此,协程可以比线程更高效地完成任务。在我上篇的文章已经讲解了:IO模型和协程介绍

1)生成器与协程关系

生成器和协程是相关但有所不同的概念。生成器是一种特殊的迭代器,可以生成一系列的值,每次迭代时只返回一个值。生成器可以使用 yield 关键字来暂停执行,并在下一次调用时继续执行。

  • 协程是一种并发编程技术,可以在单一线程中实现多个任务的并行执行。与线程不同,协程不需要创建新的系统级线程,并且消耗的资源也比线程更少。协程可以使用 yield 关键字来暂停执行,并在下一次调用时继续执行。
  • 因此,生成器可以用于实现协程,但它们不是协程的必需条件。在 Python 中,可以使用 asyncio 库来实现协程,该库不需要使用生成器。不过,在实现协程时,生成器确实可以作为一种有效的工具,帮助开发者实现协程的暂停和恢复。

2)协程实现原理

协程的实现主要是通过一种叫做 “协程调度器” 的技术实现的,这种技术能够在不创建新的线程的情况下,在单一线程中按需切换协程的执行。协程调度器会管理当前正在运行的协程以及等待执行的协程,并在每个协程执行完后切换到下一个协程。

3)协程实现方式

在 Python 中,可以使用 asyncio 库来实现协程。协程函数可以使用 async 关键字标记,表示该函数是一个协程函数。在协程函数中,可以使用 await 关键字等待其他协程完成,从而实现协程的切换。

在 Python 中,可以使用 asyncio 库来实现协程。下面是一个简单的例子:

import asyncio

async def task1():
    print("Task 1 is running")
    await asyncio.sleep(1)
    print("Task 1 is complete")

async def task2():
    print("Task 2 is running")
    await asyncio.sleep(1)
    print("Task 2 is complete")

async def main():
    task1_coro = task1()
    task2_coro = task2()
    await asyncio.gather(task1_coro, task2_coro)

if __name__ == "__main__":
    asyncio.run(main())

上面的代码中,task1task2 是两个协程函数,它们可以在单独的任务中并行执行。main 函数是协程的入口,在这个函数中,我们创建了两个协程的对象 task1_corotask2_coro,并通过 asyncio.gather 函数将它们并行执行。最后,我们通过 asyncio.run 函数启动了整个协程。

运行上面的代码,可以得到以下输出:

Task 1 is running
Task 2 is running
Task 1 is complete
Task 2 is complete

可以看到,协程中的任务是并行执行的,因此我们可以充分利用 CPU 的资源,提高程序的执行效率。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/xiaofan797/python-advanced-document.git
git@gitee.com:xiaofan797/python-advanced-document.git
xiaofan797
python-advanced-document
python高级文档
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891