回到顶部 Fork me on GitHub

python学习之【第十三篇】:Python中的生成器

1.为什么要有生成器?

在Python中,通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。那么此时聪明的你肯定会这么想:有没有一种机制,当我们想要创建列表时,不要直接先把列表中元素全部创建出来,而是把列表元素生成的方法给我,当我要使用哪几个元素的时候我再生成,现做现卖,用多少做多少,这样多好。其实,这些前人们早就替我们想到了,所以,生成器应运而生。

2.什么是生成器?

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

3.如何创建生成器?

要创建一个生成器generator,有很多种方法。我们介绍两种常用的方法:

  1. 简单生成器。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个生成器generator
  2. 生成器函数。如果推算的算法比较复杂,用类似列表生成式无法实现的时候,还可以用函数来实现,这就是生成器函数。

4. 生成器表达式

只要把一个列表生成式的[ ]改成( ),就创建了一个生成器表达式generator

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

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

因为generator也是可迭代对象,所以可以使用for循环来迭代:

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

5. 生成器函数

在上述的简单生成器中,元素推算的算法仅仅是x*x,比较简单。但是如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,我们可以用函数来实现。这个定义元素推算算法的函数我们称之为生成器函数。

生成器函数与普通函数有一个很重要的区别,那就是:生成器函数中需要return的地方通通使用yield来替换。

yield关键字

yield关键字,其作用和return的功能差不多,就是返回一个值给调用者,只不过有yield的函数返回值后函数依然保持调用yield时的状态,当下次调用的时候,在原先的基础上继续执行代码,直到遇到下一个yield或者满足结束条件结束函数为止。

我们可以看以下例子:

首先定义一个生成器函数,调用该生成器函数时,返回一个生成器对象generator,然后用next()函数不断获得下一个返回值,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(g)就报错。

同样的,使用生成器函数,我们基本上也不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

def test():
    print('step1')
    yield 1
    print('step2')
    yield 2
    print('step3')
    yield 3


g = test()

for i in g:
    print(i)
# 输出
# step1
# 1
# step2
# 2
# step3
# 3

6.生成器函数与普通函数的区别

生成器函数与普通函数主要有以下不同:

  • 生成器函数包含一个或者多个yield
  • 当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行;
  • __iter__()__next__()方法等是自动实现的,所以我们可以通过next()方法对对象进行迭代;
  • 一旦函数被yield,函数会暂停,控制权返回调用者;
  • 局部变量和它们的状态会被保存,直到下一次调用;
  • 函数终止的时候,StopIteraion会被自动抛出;

7.应用

咋一看,生成器好像没有什么用啊,其实不然,很多情况下元素是无穷无尽的,例如斐波那契数列,杨辉三角等,这些元素都是不能被穷举的,所以我们无法将所有元素都放到一个列表里,只能是保存元素推算的算法,再进行逐个推算,这就是生成器的好处。

例如生成杨辉三角的代码,把每一行看做一个list,试写一个generator,不断输出下一行的list

          1
         / \
        1   1
       / \ / \
      1   2   1
     / \ / \ / \
    1   3   3   1
   / \ / \ / \ / \
  1   4   6   4   1
 / \ / \ / \ / \ / \
1   5   10  10  5   1
# 杨辉三角
def triangles():
    old_list = []
    new_list = []
    while True:
        length = len(old_list)
        if length == 0:
            new_list.append(1)
        else:
            for item in range(length + 1):
                if item == 0:
                    new_list.append(1)
                elif item == length:
                    new_list.append(1)
                else:
                    tmp = old_list[item - 1] + old_list[item]
                    new_list.append(tmp)
        yield new_list
        old_list = new_list.copy()
        new_list.clear()

(完)

posted on 2018-10-09 11:30  难凉热血,码梦为生!  阅读(177)  评论(0编辑  收藏  举报

导航