说到生成器,首先要介绍列表生成式
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]吃了