Python生成器
【转载】传智python就业班
1.引入生成器的原因:
通过列表生成式,我们可以生成一个列表,但是,受内存的限制,列表容量肯定是有限的,如创建一个包含100万个数据的列表,而且,只需要前几个元素时,那么后面的就会造成浪费,所以,如果列表元素可以按照某种算法推算出来,那么我们是否可以在循环过程中不断的推算后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator
2.下面介绍生成器的创建方法
1)第一种方法:只要把⼀个列表⽣成式的 [ ] 改成 ( ),可以把创建的生成器当成一个对象。
如图,a是一个列表,b是一个生成器。
那么我们怎么获取生成器里的元素呢?可以通过 **next()**
函数获得⽣成器的下⼀个返回值
如图,每运行一次next(b)就会生成一个值,当生成最后一个值之后想要再生成的话就有异常因为无法再生成了,你却让它生成肯定不行。每次调⽤ nextb) ,就计算出 b的下⼀个元素的值,直到计算到最后⼀个元素,没有更多的元素时,抛出 StopIteration 的异常。
当然,这种不断调⽤ next() 实在是太变态了,正确的⽅法是使⽤ for 循环,因为⽣成器也是可迭代对象。。所以,我们创建了⼀个⽣成器后,基本上永远不会调⽤ next() ,⽽是通过 for 循环来迭代它,并且不需要关⼼StopIteration 异常。
2)第二种方法如果推算的算法⽐较复杂,⽤类似列表⽣成式的 for 循环⽆法实现的时候,还可以⽤函数来实现。
我们来研究斐波那锲数列1, 1, 2, 3, 5, 8, 13, 21, 34, …
仔细观察,可以看出creatnum()函数实际上是定义了斐波拉契数列的推算规则,可以从第⼀个元素开始,推算出后续任意的元素,这种逻辑其实⾮常类似generator
。要把creatnum()函数变成generator,只需要把print(b)改为**yield b*
*就可以了
如上图,运行这个脚本时,没有任何输出,连---------1---------都没输出,这说明creatnum()根本就没被调用。这是因为,含有yield的函数不能再当成函数来看待了,它变成了生成器了,creatnum()创建了一个生成器对象。
现对程序进行修改:
用m接收这个生成器对象,打印m发现m确实是个生成器。
然后执行一个next(m),发现,运行结果出现------1------和------2------,但每个只出现了一次,没有循环下去,这是为什么呢?因为,因为程序只有一个next(b)时运行到yield时就会停止,后面的就不能运行,停止的同时会返回yield后面的b的值(value接收打印出这个值)。所有每次运行到yield时,都做两件事,一件事是返回b的值,二是在yield位置停止程序。
每执行一次next(m),才继续从刚才停止的位置向下继续运行。
也可以使用m.__next__()
方法
使用send()
⽣成器的特点:
- 节约内存
- 迭代到下⼀次的调⽤时,所使⽤的参数都是第⼀次所保留下的,即是说,在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的,⽽不是新创建的python基础语⾔。
咸鱼 作于2019.3.4 西安