刘刚刚的个人博客

python-迭代器、生成器、协程

创建时间:2022-12-25 00:52:41
更新时间:2022-12-25 00:52:41

在了解yield与协程的关系时,进行了差缺补漏

迭代器

Python中当容器对象提供了对迭代的支持时,可以通过container.__iter__()来返回一个迭代器对象。

迭代器需要支持以下两个方法,这两个方法共同构成了迭代器协议:

  1. iterator.__iter__()

    该方法返回迭代器本身,这个方法是配合forin使用所必须的。

  2. iterator.__next__()

    该方法返回下一项,如果已没有可返回的内容则引发 StopIteration异常

生成器

当一个函数内存在yield关键字时,这个函数便成为一个生成器函数。

当一个生成器函数被调用时,变会返回一个名称为生成器的迭代器。

生成器支持以下方法:

  1. 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
    关闭
  2. 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
    关闭
  3. 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
    关闭
  4. 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

生成器与迭代器的联系与区别

  1. 概念的区别

    生成器是使用了yield关键字的函数

    迭代器是实现了迭代器协议的对象

  2. 用途

    迭代器是访问容器的一种方式

    生成器是一种生成元素的方法,但生成器也实现了迭代器协议

  3. 两者的关联

    生成器也是一种迭代器

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时自动切换

我的名片

昵称:shuta

职业:后台开发(python、php)

邮箱:648949076@qq.com

站点信息

建站时间: 2020/2/19
网站程序: ANTD PRO VUE + TP6.0
晋ICP备18007778号