• python 生成器(一):生成器基础(一)生成器函数


    前言

    实现相同功能,但却符合 Python 习惯的方式是,用生成器函数代替SentenceIterator 类。
    示例 14-5 sentence_gen.py:使用生成器函数实现 Sentence 类

    import re
    import reprlib
    
    RE_WORD = re.compile('w+')
    
    class Sentence:
    
        def __init__(self, text):
            self.text = text
            self.words = RE_WORD.findall(text)
    
        def __repr__(self):
            return 'Sentence(%s)' % reprlib.repr(self.text)
    
        def __iter__(self):
            for word in self.words:  ➊
                yield word  ➋
            return# 完成! ➍

    ❶ 迭代 self.words。
    ❷ 产出当前的 word。
    ❸ 这个 return 语句不是必要的;这个函数可以直接“落空”,自动返回。不管有没有 return 语句,生成器函数都不会抛出 StopIteration异常,而是在生成完全部值之后会直接退出。
    ❹ 不用再单独定义一个迭代器类!

    在示例 14-4 定义的 Sentence 类中,__iter__ 方法调用SentenceIterator 类的构造方法创建一个迭代器并将其返回。
    而在示例 14-5 中,迭代器其实是生成器对象,每次调用 __iter__ 方法都会自动创建,因为这里的 __iter__ 方法是生成器函数。

    生成器函数的工作原理

    只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。
    调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。

    简单的生成器函数

    >>> def gen_123():  #
    ...     yield 1  #
    ...     yield 2
    ...     yield 3
    ...
    >>> gen_123  # doctest: +ELLIPSIS
    <function gen_123 at 0x...>  #
    >>> gen_123()   # doctest: +ELLIPSIS
    <generator object gen_123 at 0x...>  #
    >>> for i in gen_123():  #
    ...     print(i)
    1
    2
    3
    >>> g = gen_123()  #
    >>> next(g)  #
    1
    >>> next(g)
    2
    >>> next(g)
    3
    >>> next(g)  #
    Traceback (most recent call last):
      ...
    StopIteration

    ❶ 只要 Python 函数中包含关键字 yield,该函数就是生成器函数。
    ❷ 生成器函数的定义体中通常都有循环,不过这不是必要条件;这里我重复使用 3 次 yield。
    ❸ 仔细看,gen_123 是函数对象。
    ❹ 但是调用时,gen_123() 返回一个生成器对象。
    ❺ 生成器是迭代器,会生成传给 yield 关键字的表达式的值。
    ❻ 为了仔细检查,我们把生成器对象赋值给 g。
    ❼ 因为 g 是迭代器,所以调用 next(g) 会获取 yield 生成的下一个元素。
    ❽ 生成器函数的定义体执行完毕后,生成器对象会抛出StopIteration 异常。


    生成器函数会创建一个生成器对象,包装生成器函数的定义体。

    把生成器传给 next(...) 函数时,生成器函数会向前,执行函数定义体中的下一个 yield 语句,返回产出的值,并在函数定义体的当前位置暂停。

    最终,函数的定义体返回时,外层的生成器对象会抛出StopIteration 异常——这一点与迭代器协议一致。

    示例 14-6 使用 for 循环更清楚地说明了生成器函数定义体的执行过程。

    >>> def gen_AB():  #
    ...     print('start')
    ...     yield 'A'       #
    ...     print('continue')
    ...     yield 'B'       #
    ...     print('end.')   #
    ...
    >>> for c in gen_AB():  #
    ...     print('-->', c)  #
    ...
    start  ➐
    --> A  ➑
    continue--> B  ➓
    end.   ⓫
    >>> ⓬

    ❶ 定义生成器函数的方式与普通的函数无异,只不过要使用 yield 关键字。

    ❷ 在 for 循环中第一次隐式调用 next() 函数时(序号➎),会打印'start',然后停在第一个 yield 语句,生成值 'A'。
    ❸ 在 for 循环中第二次隐式调用 next() 函数时,会打印'continue',然后停在第二个 yield 语句,生成值 'B'。
    ❹ 第三次调用 next() 函数时,会打印 'end.',然后到达函数定义体的末尾,导致生成器对象抛出 StopIteration 异常。
    ❺ 迭代时,for 机制的作用与 g = iter(gen_AB()) 一样,用于获取生成器对象,然后每次迭代时调用 next(g)。
    ❻ 循环块打印 --> 和 next(g) 返回的值。但是,生成器函数中的print 函数输出结果之后才会看到这个输出。
    ❼ 'start' 是生成器函数定义体中 print('start') 输出的结果。
    ❽ 生成器函数定义体中的 yield 'A' 语句会生成值 A,提供给 for 循环使用,而 A 会赋值给变量 c,最终输出 --> A。
    ❾ 第二次调用 next(g),继续迭代,生成器函数定义体中的代码由yield 'A' 前进到 yield 'B'。文本 continue 是由生成器函数定义体中的第二个 print 函数输出的。
    ❿ yield 'B' 语句生成值 B,提供给 for 循环使用,而 B 会赋值给变量 c,所以循环打印出 --> B。
    ⓫ 第三次调用 next(it),继续迭代,前进到生成器函数的末尾。文本end. 是由生成器函数定义体中的第三个 print 函数输出的。
    到达生成器函数定义体的末尾时,生成器对象抛出 StopIteration 异常。for机制会捕获异常,因此循环终止时没有报错。
    ⓬ 现在,希望你已经知道示例 14-5 中 Sentence.__iter__ 方法的作用了:__iter__ 方法(指代 Sentence.__iter__ 方法)是生成器函数,调用时会构建一个实现了迭代器
    接口的生成器对象,因此不用再定义 SentenceIterator 类了。




  • 相关阅读:
    IIS部署Asp.Net Core 项目运行时报错,处理程序“aspNetCore”在其模块列表中有一个错误模块“AspNetCoreModuleV2"
    Linux Mysql5.7.22安装
    Nginx初体验
    asp.net core Csc任务不支持SharedCompilationId参数,请确认改参数存在于此任务中,并且是可设置的公共实例属性
    【Node.js 】Express框架
    【Node.js】 初体验
    Mongodb 配置
    【C#】Windows服务守护并发送邮件通知
    新建【Git】仓库后给使用者授权
    Git提交修改的代码出现提交不上去
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12966912.html
Copyright © 2020-2023  润新知