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

  • 相关阅读:
    大数加法、乘法实现的简单版本
    hdu 4027 Can you answer these queries?
    zoj 1610 Count the Colors
    2018 徐州赛区网赛 G. Trace
    1495 中国好区间 尺取法
    LA 3938 动态最大连续区间 线段树
    51nod 1275 连续子段的差异
    caioj 1172 poj 2823 单调队列过渡题
    数据结构和算法题
    一个通用分页类
  • 原文地址:https://www.cnblogs.com/mazhiyong/p/12565728.html
Copyright © 2011-2022 走看看