• Python进阶08 生成器


    一、生成器

    通过列表推导,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    所以,如果列表元素可以按照某种算法推算出来,那我们就不必创建完整的list,从而节省大量的空间。
    在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    二、生成器方法一

    第一种方法很简单,只要把一个列表推导的[]改成(),就创建了一个generator:

    >>> L = [x * x for x in range(5)]
    >>> L
    [0, 1, 4, 9, 16]
    >>> g = (x * x for x in range(5))
    >>> g
    <generator object <genexpr> at 0x1044299a8>

    创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

    我们可以通过next()函数获得generator的下一个返回值,从而获得generator的每一个元素。

    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

    generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
    当然,遍历generator正确的方法是使用for循环,因为generator也是可迭代对象:

    >>> g = (x * x for x in range(5))
    >>> for x in g:
    ...     print(x)
    ...
    0
    1
    4
    9
    16

    我们创建了一个generator后,基本上不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

    三、生成器方法二

    如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b
            a, b = b, a + b
            n = n + 1
        return 'done'
    >>> f = fib(6)
    >>> f
    <generator object fib at 0x1044299a8>

    这里,比较难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

    >>> next(f)
    1
    >>> next(f)
    1
    >>> next(f)
    2
    >>> next(f)
    3
    >>> next(f)
    5
    >>> next(f)
    8
    >>> next(f)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration: done

    实际上,把函数改成generator后,我们也基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    >>> for n in fib(6):
    ...     print(n)
    ...
    1
    1
    2
    3
    5
    8

    四、总结

    generator是非常强大的工具,在Python中,可以简单地把列表推导改成generator,也可以通过函数实现复杂逻辑的generator。
    对于列表推导改成的generator来说,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。
    对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。

    请注意区分普通函数和generator函数,普通函数调用直接返回结果:

    >>> r = abs(6)
    >>> r
    6

    generator函数的“调用”实际返回一个generator对象:

    >>> f = fib(6)
    >>> f
    <generator object fib at 0x104429a20>

    参考文章(这篇文章比书上讲的更通俗易懂):

    https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128

  • 相关阅读:
    按学生成绩排序
    错误分析:floating point formats not linked
    学生成绩等级统计
    两个链表按升序合并
    js随机生成hex色值
    WEB监控系列第一篇:web监控搭建——graphite+statsd(单机搭建)
    WEB监控系列第四篇:statsd指南
    WEB监控系列第三篇:graphite指南
    安装Fedora 15后需做的25件事情
    《python tutorial》python 学习第二天
  • 原文地址:https://www.cnblogs.com/mazhiyong/p/12565728.html
Copyright © 2020-2023  润新知