生成器函数

写一个列表表达式
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()

好了,先这么多吧

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhaoXuWen23

你的鼓励是我的动力,持续更新中

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值