写一个列表表达式
a = [i for i in range(9)]#[0,1,2,3,4,5,6,7,8]
这个是把列表直接展示出来完了,但是呢我们可能不需要里面那么多的数据,可能我们只需要里面的一个数据,这样的话这个列表就会浪费很多的内存空间,这时候我们是否可以把这个列表的元素用一个去一个呢?答案是可以的那就是生成器
生成器怎么去表达呢,很简单就是把中括号换成小括号
a = (i for i in range(6))#<generator object at 0x034A61E0>
看看这个生成器函数打印的a是什么呢,他是一个内存地址,并没有把这个里面列表给打印出来,那我们怎么取值呢
print(a.next())#0
print(a.next())#1
print(a.next())#2
或者这样取值
print(next(a))#3
print(next(a))#4
print(next(a))#5
这是后这个列表的值已经取完了,那么我们再次取值
print(next(a))#StopIteration
这时候就会报错了,因为后面没有值了
但是在实际引用当中我们是不会这么调用函数的,太麻烦了,一般情况下我们都会只用for循环,使用for循环去遍历一个可迭代序列
但是我们会用到的是生成器函数,有时候我们不需要运行一个函数的全部内容,而是运行之后保存一个状态,然后去做其他事,再然后回到这个状态接着执行刚才的函数,这时我们就需要一个生成器函数了
def test():
#生成器函数好处是保留函数状态,下一次的结果是基于上一次的状态接着往下运行
yield 1
yield 2
yield 3
yield 4
yield 5
res = test()
print(res)#<generator object test at 0x02E21AB0>
print(res.__next__())#迭代1
print(res.__next__())#2
print(res.__next__())
print(res.__next__())
print(res.__next__())
我们发现每一次执行这个函数的next方法,yield都是保存上一次状态接着往下执行。
下面说一个具体的例子,说一个包子铺做包子,来一个人吃包子,让你用函数来表示出来,我们根据这个情况慢慢写慢慢优化,你就知道生成器这个函数的好处了
def product_baozi():
ret = []
for i in range(100):
ret.append('包子%d'%i)
return ret
def eat():
product_baozi()
print('我要吃一个包子')
print(product_baozi())
#['baozi0', 'baozi1', 'baozi2', 'baozi3', 'baozi4', 'baozi5', 'baozi6', 'baozi7', 'baozi8', 'baozi9']
eat() #我要吃一个包子
你看我们只需要吃一个包子,但是包子铺缺是需要把10个包子一下做出来之后才开始卖包子,但是呢,我今天只来了一个人,所以我吃一个包子就饱了,那剩下的包子是不是就浪费了,因为我们把包子蒸出来了。
这时候我们用下生成器函数
def product_baozi():
for i in range(5):
print('开始生产包子%d'%i)
yield '包子%d'%i
def eat():
print('我要吃一个包子')
a = product_baozi()
a = product_baozi()
print(a.__next__())#开始生产包子0,包子0
eat()#我要吃一个包子
print(a.__next__())#开始生产包子1,包子1
eat()
在接着说一个场景,公司发鸡蛋,公司有一只母鸡,给员工发福利,一人发一个鸡蛋,要求来一个人发一个鸡蛋,来一个人发一个鸡蛋,其实这个和上面卖包子是一样的
def jidan():
for i in range(10):
print('老母鸡开始下鸡蛋%d'%i)
yield ('开始发鸡蛋%d'%i)
a = jidan()
print(next(a))
print(next(a))
这样就实现了下一个鸡蛋发一个人,不会出现一下子把鸡蛋下完再发的情况
使用生成器函数的好处就是
1、生成器的好处是延迟计算,一次放回一个结果
2、生成器还能有效提高代码可读性
语法上和函数类似,生成器函数和常规函数几乎是一样的,他们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return
自动实现迭代器协议:对于生成器,Python会自动实现迭代协议,以便应用到迭代背景中,由于生成器自动实现了迭代器协议, 所以我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生stopiteration异常
状态挂起:生成器使用yield语句返回一个值,yield语句挂起该生成器函数状态,保留足够的信息,以便之后从他离开的地方继续执行
注意生成器只能遍历一次(母鸡一生只能下一定数量的蛋,下完就死掉了)
下面再说一个例子,是实现并发吃包子的场景,就是同时来两个人吃包子的场景:
import time
def customer(name):
print('我是%s,我准备吃包子了'%name)
while True:
baozi = yield
print('%s开心的再吃%s的包子'%(name,baozi))
def product_baozi():
c1 = customer('xiaoming')
c1.__next__()
c2 = customer('xaiohogn')
c2.__next__()
for i in range(10):
time.sleep(1)
c1.send('包子%s'%i)#这个地方传什么值那么baozi的值就是什么 但是customer这个函数返回的值为空
c2.send('包子%s'%i)
product_baozi()
好了,先这么多吧