概念辨析
生成器其实就是一种特殊的迭代器。它使一种更为高级、更为优雅的迭代器。
仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。
这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。
生成器主要的特点就是:延迟操作,在需要的时候产生结果,而不是立即产生结果。
列表生成器
#先写一个列表a,可以随意取数据 a = [1,2,3,4,5] print(a[0]) #打印1 #再生成一个列表,也可以随意取数据 b = [i+1 for i in range(5)] print(b[3]) #打印3 #再生成一个生成器列表,无法随意取数据了 c =(i+1 for i in range(5)) print(c(3)) #打印 生成器c is not callble
#不能随意访问任意值。只能依次取
上面例子中的列表c被称为列表生成式,思考一下,这个式子很像我之前自己写的迭代器,都是通过一种算法来实现一串数据的生成,遗憾的是列表生成器中的算法是固定的:for语句控制下有限的几种变化。
是不是可以还是像写迭代器一样,把算法灵活化、开放化?同时保留列表生成器延迟这个特性?
这就诞生了生成器函数的需求。
那么要如何写一个生成器函数?
同时,留意到在定义上其实生成器是迭代器的子集,即生成器具有_iter_()和_next_()方法,那么在构想生成器的类时,不用再重复写这两个功能,只需要在_next_()中写明算法;再加上一个类似于可以随时暂停的功能——
yield,就是这个功能。yield如果出现在一个函数中,相当于为这个函数增添了三个功能:_iter_()和_next_()方法,以及挂起功能。即拥有yield的函数就是一个生成器——注意,生成器是拥有yield的函数的返回值,而不是这个函数自己。
(我认为语法糖就是一个打包的意思,把一堆功能浓缩为一个符号)
综上,一个生成器函数就写成了。
一个思考不知道对不对
在写一个迭代器的时候,会随着迭代器的调用迭代出所有值,加入有一万个值,就会非常浪费内存和时间。于是想,能不能有一种机制,把某一个值保存起来,需要时再用。
保存在哪儿?生成器中。把这种包含了迭代器功能同时还能保存值得,叫做生成器。
生成器
生成器函数的返回值,就是生成器。
先抄一些生成器的干货:
1.生成器的唯一注意事项就是:生成器只能遍历一次。
2.使用生成器以后,代码行数更少。——不看过程,只看结果
3.生成器表达式能做的事情列表解析基本都能处理,只不过在需要处理的序列比较大时,列表解析比较费内存。生成器可以通过延迟来减少内存占用和计算速度。
4.yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。
5.每次循环都会执函数内部的代码,执行到 yield语句 时, 函数就返回一个迭代值,下次迭代时,代码从 yield的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
6.yield就是暂停/挂起的作用。生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时(调用next)返回一个值,直到遇到StopIteration异常结束。