zoukankan      html  css  js  c++  java
  • 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 类了。




  • 相关阅读:
    Apache 虚拟主机 VirtualHost 配置
    EAX、ECX、EDX、EBX寄存器的作用
    Python中文文档 目录(转载)
    八度
    POJ 3268 Silver Cow Party (最短路)
    POJ 2253 Frogger (求每条路径中最大值的最小值,Dijkstra变形)
    2013金山西山居创意游戏程序挑战赛——复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560 我是歌手
    HDU 4549 M斐波那契数列(矩阵快速幂+欧拉定理)
    UVA 11624 Fire! (简单图论基础)
    HDU 3534 Tree (树形DP)
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12966912.html
Copyright © 2011-2022 走看看