说到生成器,首先要介绍列表生成式
a = [ i +1 for i in range(10)] print(a)
执行结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
上面的a = [ i+1 for i in range(10)]就叫做列表生成式
通过列表生成式,我们可以直接创建一个列表。此时,列表的数据是固定的,并且可以直接访问的。但是,由于内存大小的限制,列表的容量是有限的。
并且,如果创建一个包含100w个元素的列表,不仅需要占用很大的内存空间,而且,如果仅仅需要使用其中的几个数据的话,那其余的绝大部分数据所占用的空间就白白浪费了。
所以,如果列表元素可以按照某中算法推算出来,这样我们就不必创建完成的立标,而是在循环的过程中不断推算出后续的数据,从而节省大量的空间和时间。
这种机制就是python中的生成器:generator
generator的创建:
a = (i +1 for i in range(10)) print(a)
执行结果:
<generator object <genexpr> at 0x000001798835E2B0>
此时,a就是一个生成器了
创建List和generator的区别仅仅在于最外层的[]和()
但generator中的数据是只有在访问时才会生成的,而不是像list中已经准备好的
生成器中数据的访问方式:
1、通过next()方法访问:
print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a)) print(next(a))
执行结果:
1 2 3 4 5 6 7 8 9 10 StopIteration
由于generator保存的是算法,每次调用next(),计算的是下一个元素的值,直到计算到最后一个元素,当没有更多的元素时,抛出StopIteration的错误
2、通过next()的方法访问生成器的数据是反人类的,一般通过for循环的方式访问:
for i in a: print(i)
执行结果:
1 2 3 4 5 6 7 8 9 10
generator的函数实现:
我们可以使用下面的函数打印斐波那契数列(Fibonacci)
def func(max): a,b,n = 0,1,0 while n < max : print(b) a,b = b, a+b n += 1 res = func(10)
注意,在上述函数中的赋值语句:a, b
=
b, a
+
b
等同于:
t = (b,a+b) #t是一个元祖 a = t[0] b = t[1]
而不是:
a = b b = a+b
执行结果:
1 1 2 3 5 8 13 21 34 55
func函数实际上是定义了斐波拉契数列的算法规则,可以从第一个元素开始,算出后续任意的元素,这种逻辑已经非常接近于generator
要把上面的func函数转变为generator,只需要把print(b)变成yiled b即可:
def func(max): a,b,n = 0,1,0 while n < max : yield b #保存了函数的中断状态,返回当前状态的值 a,b = b, a+b n += 1 res = func(10) print(res)
执行结果:
<generator object func at 0x000001BA2575E2B0>
当一个函数中包含yield关键字时,此时,这个函数就不再是一个普通的函数,而是一个generator
函数和generator的执行流程是不太一样的:
函数是顺序执行,遇到return或者最后一行函数语句就返回
但是变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次从上次返回的yield语句处继续执行
我们还可以通过yield来实现在单线程情况下的并发运算效果(协程)
#生产者消费者实例 import time def consumer(name): print ('%s 准备吃包子啦!' % name) while True: bao = yield print ('包子[%s]来了,被[%s]吃了'% (bao,name)) def producer(name): c1 = consumer('A') c2 = consumer('B') c3 = consumer('C') c1.__next__() c2.__next__() next(c3) print ('开始做包子啦!!!') for i in range(1,10): time.sleep(1) print ('做了1个包子!') c1.send(i) c2.send(i) c3.send(i) producer('HAHA')
运行结果:
A 准备吃包子啦! B 准备吃包子啦! C 准备吃包子啦! 开始做包子啦!!! 做了1个包子! 包子[1]来了,被[A]吃了 包子[1]来了,被[B]吃了 包子[1]来了,被[C]吃了 做了1个包子! 包子[2]来了,被[A]吃了 包子[2]来了,被[B]吃了 包子[2]来了,被[C]吃了 做了1个包子! 包子[3]来了,被[A]吃了 包子[3]来了,被[B]吃了 包子[3]来了,被[C]吃了 做了1个包子! 包子[4]来了,被[A]吃了 包子[4]来了,被[B]吃了 包子[4]来了,被[C]吃了 做了1个包子! 包子[5]来了,被[A]吃了 包子[5]来了,被[B]吃了 包子[5]来了,被[C]吃了 做了1个包子! 包子[6]来了,被[A]吃了 包子[6]来了,被[B]吃了 包子[6]来了,被[C]吃了 做了1个包子! 包子[7]来了,被[A]吃了 包子[7]来了,被[B]吃了 包子[7]来了,被[C]吃了 做了1个包子! 包子[8]来了,被[A]吃了 包子[8]来了,被[B]吃了 包子[8]来了,被[C]吃了 做了1个包子! 包子[9]来了,被[A]吃了 包子[9]来了,被[B]吃了 包子[9]来了,被[C]吃了