在了解生成器之前,必须要了解一下:列表生成式
生成器:generator
通过列表生成式,列表元素可以一个个推算出来,但当他算出来全部存放着的时候,还是很占用空间。
那我们是否可以在循环过程中不断推算,要用到多少,算到多少,这样就可以节省大量空间。
在Python中,这种一边循环一边计算的机制,叫做生成器。
生成器如何定义?
定义方式一:把上面列表生成式的最外层中括号改成小括号:( i * 2 for i in range(10))
#生成器generator
#定义方式一:
a = ( i * 2 for i in range(10))
print(a) #打印a实际是一个生成器地址
print(a.__next__()) #打印第一个
print(a.__next__()) #打印第二个
for i in a: #打印接下来的所有
print(i)
定义方式二:在函数中加入yield关键字。
#方式二:函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
#print(b) #打印每个fib数
yield b #写了yield的函数,就是生成器,就不是函数了
a, b = b, a + b
n += 1
return 'done' #当超过了就会返回这个
a = fib(10) #赋值给a
print(a) #打印a看一下,实际a是一个生成器(generator)地址
生成器如何使用?
方法一:__next__()方法
#使用方法一:
print(a.__next__())
print(a.__next__())
print(a.__next__()) #打印3次
方法二:循环打印
for i in a: #循环打印出来
print(i)
方法三:while循环,同时可以抓错误
while True:
try:
x = next(a)
print(x)
except StopIteration as e: #当超出的时候抓住错误,并显示生成器的return值
print(e.value)
break
注意:1、生成器只有在调用时才会生成相应的数据。
2、生成器只记录当前位置。
3、使用只有一个__next__()方法。
生成器的优点?
因为生成器是一次次出来的,所以他的好处就是:
1、可以在中间加入别的东西而不必等全部执行完。2、可以用返回值抓住异常进行处理。
3、还可通过yield实现在单线程的情况下实现并发运算的效果。(下面给个例子)
大家体会一下下面这段代码,通过producter函数里面使用consumer生成器,实现做一个吃一个的效果,实现并发运算的效果。
def consumer(name):
while True:
food = yield
print('%s吃了%s号食物'%(name, food))
def producter(name):
print('%s开始做吃的了'%name)
c1 = consumer('a')
c2 = consumer('b')
c1.__next__() #第一次要next一下才能走到生成器的yield位置
c2.__next__()
for i in range(10):
print('----------------------')
print('做好了%s号吃的'%i)
c1.send(i) #传给yield
c2.send(i)
producter('lkh')
这段代码使用send方法,把其他的值传入生成器中的yield,并且赋值给food。同时通过循环,每次发送每次执行consumer生成器,实现模拟了一个并发效果。虽然通过使用函数一样可以实现上面的例子,但当consumer里有不断递进的逻辑的时候,比如有一个递推公式在里面,从而导致每次运算的值不一样的时候,生成器就有很大作用了(主要是为了节省空间而和影响效率,试想,如果一个函数运行要花10分钟算完,那需要一下算一下的体验会远远大于算完再调用的体验)。