python-迭代器、生成器、协程
迭代器
Python中当容器对象提供了对迭代的支持时,可以通过container.__iter__()
来返回一个迭代器对象。
迭代器需要支持以下两个方法,这两个方法共同构成了迭代器协议:
iterator.__iter__()
该方法返回迭代器本身,这个方法是配合
for
和in
使用所必须的。iterator.__next__()
该方法返回下一项,如果已没有可返回的内容则引发
StopIteration
异常
生成器
当一个函数内存在yield
关键字时,这个函数便成为一个生成器函数。
当一个生成器函数被调用时,变会返回一个名称为生成器的迭代器。
生成器支持以下方法:
generator.__next__()
有两个作用:
(1)开始一个生成器函数的执行
(2)从上次执行yield的地方恢复,如果为该用法,则上次的yield表达式的值,会被赋值为None.
from icecream import ic def echo(value=None): print("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: value = e finally: print("关闭") generator = echo(1) ic(next(generator)) ic(next(generator)) #输出结果: >>> 生成器开始执行 ic| next(generator): 1 ic| next(generator): None 关闭
generator.send()
有两个作用:
(1)开始一个生成器函数的执行,此时send的参数比如为None
(2)从上次执行yield的地方恢复,如果为该用法,则上次的yield表达式的值,会被赋值为send的参数.
from icecream import ic def echo(value=None): print("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: value = e finally: print("关闭") generator = echo(1) print("---") ic(generator.send(None)) ic(generator.send(1)) # 输出结果 >>> --- 生成器开始执行 ic| generator.send(None): 1 ic| generator.send(1): 1 关闭
generator.throw()
在生成器暂停的位置引发一个异常,并返回该生成器函数产生的下一个值,如果没有下一个值,则会引发
StopIteration
异常# 引发一个异常,有下一个值时的示例 def echo(value=None): ic("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: value = e finally: print("关闭") generator = echo(1) ic("---") ic(next(generator)) ic(generator.throw(TypeError, "spam")) >>> ic| '---' ic| '生成器开始执行' ic| next(generator): 1 ic| generator.throw(TypeError, "spam"): TypeError('spam') 关闭
# 引发一个异常,无下一个值时的示例 def echo(value=None): ic("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: break finally: print("关闭") generator = echo(1) ic("---") ic(next(generator)) ic(generator.throw(TypeError, "spam")) >>> ic| '---' ic| '生成器开始执行' ic| next(generator): 1 Traceback (most recent call last): File "E:\2022www\test_3.7\main.py", line 42, in <module> ic(generator.throw(TypeError, "spam")) StopIteration 关闭
generator.close()
在生成器函数暂停的位置引发
GeneratorExit
异常,如果此时生成器再产生一个值,则会引发RuntimeError
,如果生成器已经由于异常或者正常退出,则不会引发异常.# 引发 `GeneratorExit`异常的示例 def echo(value=None): ic("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: break finally: traceback.print_exc() print("关闭") generator = echo(1) ic("---") ic(next(generator)) ic(generator.close()) >>> ic| '---' ic| '生成器开始执行' ic| next(generator): 1 Traceback (most recent call last): File "E:\2022www\test_3.7\main.py", line 32, in echo value = (yield value) GeneratorExit ic| generator.close(): None 关闭
# 引发 `RuntimeError`异常的示例 def echo(value=None): ic("生成器开始执行") try: while True: try: value = (yield value) except Exception as e: break finally: yield 1 print("关闭") generator = echo(1) ic("---") ic(next(generator)) ic(generator.close()) >>> ic| '---' ic| '生成器开始执行' ic| next(generator): 1 Traceback (most recent call last): File "E:\2022www\test_3.7\main.py", line 43, in <module> ic(generator.close()) RuntimeError: generator ignored GeneratorExit
生成器与迭代器的联系与区别
概念的区别
生成器是使用了yield关键字的函数
迭代器是实现了迭代器协议的对象
用途
迭代器是访问容器的一种方式
生成器是一种生成元素的方法,但生成器也实现了迭代器协议
两者的关联
生成器也是一种迭代器
def echo(value=None):
ic("生成器开始执行")
yield 1
ec = echo(1)
from collections.abc import Iterator, Generator
#
print(type(ec))
print(ec.__class__.mro())
print(isinstance(ec, Iterator))
print(isinstance(ec, Generator))
>>>
<class 'generator'>
[<class 'generator'>, <class 'object'>]
True
True
[<class 'collections.abc.Iterator'>, <class 'collections.abc.Iterable'>, <class 'object'>]
[<class 'collections.abc.Generator'>, <class 'collections.abc.Iterator'>, <class 'collections.abc.Iterable'>, <class 'object'>]
tip:
此处的通过python的类的mro与abc中的mro不同,尚未找到原因,猜测可能是因为cpython的原因.
列表生成式与生成器推导式
列表生成式直接生成的就是列表
生成器推导式为生成器
a = [i for i in range(10)] # 列表生成式
b = (i for i in range(10)) # 生成器推导式
ic(a,type(a))
ic(b,type(b))
>>>
ic| a: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], type(a): <class 'list'>
ic| b: <generator object <genexpr> at 0x035F7CF0>
type(b): <class 'generator'>
可迭代对象与迭代器的关系
可迭代对象实现了__iter__()
方法,可以将可迭代对象转为迭代器.
yield与协程
yield在生成器中实现了保存程序运行状态时上下文的功能,其作用与协程一样.
但是其并无法在遇到io时自动切换