决战Python之巅(十三)

前言

拖了这么久才来补生成器和迭代器- -

知识回顾

列表生成式

之前在讲列表的时候并没有经过这个东西,现在我来介绍一下。
列表生成式可以用一句话就能生成一个列表,如a = [x for x in range(10)],这样就能直接生成一个0~9的列表。相对于利用for循环、while循环来说,更简单快捷。当然你也可以这样做b = [x**2 for x in range(10)],注意前面公式最多支持三元运算。

生成器

现在,我有一个需求,即生成一个有100000000(1亿)个元素的列表,如果像上面一样:d = [x for x in range(100000000)],一般的电脑请不要轻易尝试,这种放纵的滋味会让你的电脑死机。壕随意- -。但是即使配置好也发现需要等待一段时间才能给出结果,即使你不用列表生成式,用循环做也一样。这是因为列表生成式总会把元素全都创建好才会返回,如果数据大一点,那将会占用很多内存,但我们实际使用的可能只有里面的一小部分,这样便做了很多无用功。
那么,怎么应对这种情况呢?有这么一个东西,可以将列表元素按照你规定的算法算出,但只有你需要的时候才会生成,你用多少调多少,岂不快哉?
这就是生成器,官方解释如下:

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

那怎么创建呢?

生成器创建方式一

与列表生成式类似,a = (x for x in range(10)),区别仅仅在于中括号变成了小括号。
在这里插入图片描述
这样,你就有了一个生成器。那怎么用这个生成器呢?这里我们要用到一个next()语法,字面意思就是下一个,我们来试下效果:
在这里插入图片描述
每调用一次next(a),便返回一个元素。注意这里只能不断向前进,不能后退,即当next(a)返回3的时候,你不可能在next(a)返回2出来,前面的过了就过了,不能再回去了。
当你next返回最后一个元素后,再次next,会有什么效果?
在这里插入图片描述
这里会抛出一个StopIteration的异常,告诉你生成器已经到头了,再接下去就没有了。(这个当我们后面学了捕捉异常之后另有用处)。
当然,一次次调用next(a)不太现实,这里我们可以结合循环来做:
在这里插入图片描述
你会发现,当使用for循环的时候,就算到了尽头,也不会报错,即不需要关心StopIteration的错误。

生成器创建方式二

有其一必有其二。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
我们用斐波拉契函数做示范:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

…不知道什么是斐波拉契的寄几百度去。
现在我们的fib()距离生成器只有一步之遥:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
   # return 'done'

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
在这里插入图片描述
生成器与函数的执行流程不太一样:
在这里插入图片描述
在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。可以使用==isinstance()==判断一个对象是否是Iterable对象:
在这里插入图片描述
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
在这里插入图片描述
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
在这里插入图片描述
你可能会问,为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

猜你喜欢

转载自blog.csdn.net/qq_33267875/article/details/85040727