高级特性之生成器

1.什么是生成器
以下引用廖雪峰的官方网站

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

2.生成器的创建

(1)将列表生成式的【】变成()

#列表生成式,当生成时元素即打印, 会占用内存,
In [2]: [i for i in range(1,10)]  
Out[2]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

#生成器类型,并不会打印数据
In [3]: (i for i in range(1,10))
Out[3]: <generator object <genexpr> at 0x12bde10>
  • 读取生成器元素的第一个方式——–next()
#当我们把这个生成器赋给一个变量
In [4]: g = (i for i in range(1,10))

#为该变量使用next()函数获得generator的下一个返回值:
In [5]: g.next()
Out[5]: 1

In [6]: g.next()
Out[6]: 2

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

  • 读取生成器元素的第二个方式——for循环
#生成器也是可迭代对象,可以使用for循环
In [8]: g = (i for i in range(1,10))  

In [9]: for i in g:
    print i
   ...:     
1
2
3
4
5
6
7
8
9

(2)定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, …

def fib(max):
    n,a,b = 0,0,1  
    while n < max:
        yield b
        a,b = b,a+b
        n +=1
g = fib(6)
for i in g:
    print i

Tips—–python中的两值交换
x=3、y=4
执行:

x, y = y, x

实质上是 先构造右边的元组(y,x),即(4,3),然后将元组的值依次赋给x,y;

回到Fibonacci的例子上,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代。
那么该generator的函数的执行流程是:
每次在遇到for循环(可以理解为每次读取生成器元素的时候)的时候执行函数(也就是fib(max)函数),遇到yield语句返回,再次执行时从上次返回的的yield语句处执行,读者可以将上面的程序单步调试,就能更深刻理解。

3.生成器的应用
(1)实现生产者消费者模型(有无缓冲区)
???什么时生产者消费者模型
生产者和消费者之间用中间类似一个队列一样的东西串起来。这个队列可以想像成一个存放产品的“仓库”,生产者只需要关心这个“仓库”,并不需要关心具体的消费者,对于生产者而言甚至都不知道有这些消费者存在。对于消费者而言他也不需要关心具体的生产者,到底有多少生产者也不是他关心的事情,他只要关心这个“仓库”中还有没有东西。
这里写图片描述

  • 无缓冲区(没有中间商)
#生成器
def consumer(name):
    print "[%s]准备买sugar" %(name)
    while True:
        size = yield
        print "客户[%s]买[%s]包装的sugar" %(name,size)

#引用time模块,才可以使用下面的sleep方法
import time     
def producer(name,*size):
    #调用生成器赋给变量c1
    c1 = consumer("user1")     
    c2 = consumer("user2")
    #c1使用next()方法后才可以执行consumer(),遇到yield停止
    c1.next()                  
    c2.next()                  
    print "准备制作sugar....."
    for i in size:
        time.sleep(1)       #等待一秒
        print "[%s]制作了[%s]包装的sugar提供给消费者" %(name,i)
        #从上次停止的yield处继续执行
        c1.send(i)     #send 方法将值传给 yield 所在位置的变量     
        c2.send(i)
producer("producer1","大","中","小")

运行效果:

[user1]准备买sugar
[user2]准备买sugar
准备制作sugar.....
[producer1]制作了[大]包装的sugar提供给消费者
客户[user1][大]包装的sugar
客户[user2][大]包装的sugar
[producer1]制作了[中]包装的sugar提供给消费者
客户[user1][中]包装的sugar
客户[user2][中]包装的sugar
[producer1]制作了[小]包装的sugar提供给消费者
客户[user1][小]包装的sugar
客户[user2][小]包装的sugar
  • 有缓冲区(有中间商)
#定义一个中间商队列
sugar_agency = []

def consumer(name):
    print "[%s]准备买sugar" %(name)
    while True:
        size = yield
        #将user已经买过的size从sugar_agency队列中移除
        sugar_agency.remove(size)  
        print "客户[%s]买[%s]包装的sugar" %(name,size)

def producer(name,*size):
    print "准备制作sugar....."
    for i in size:
        time.sleep(1)
        print "[%s]制作了[%s]包装的sugar提供给消费者" %(name,i)
        sugar_agency.append(i)

producer("producer1","大","中","小")  #顺序执行完producer()
c1 = consumer("user1")               #调用生成器赋给变量c1
c1.next()                            #从上次停止的yield处继续执行
#send 方法将值传给 yield 所在位置的变量 
c1.send("大")                           
c2 = consumer("user2")
c2.next()
c2.send("中")
print "还剩包装:"                    #显示user购买完后,还剩的size
for i in sugar_agency:
    print i

运行效果:

准备制作sugar.....
[producer1]制作了[大]包装的sugar提供给消费者
[producer1]制作了[中]包装的sugar提供给消费者
[producer1]制作了[小]包装的sugar提供给消费者
[user1]准备买sugar
客户[user1][大]包装的sugar
[user2]准备买sugar
客户[user2][中]包装的sugar
还剩包装:
小

(2)聊天机器人

def chat_robot():
    res = ""
    while True:
        received = yield res
        if "你好" in received or "hi" in received:
            print "你好呀!!!欢迎来到yueer的世界"
        elif "笨" in received or "傻" in received:
            print "请文明用语哦!!!我会生气的,哼~~~"
        elif "性别" in received or "女" in received:
            print "本姑娘还小,没有谈过恋爱!!!傲娇脸"
        elif "我的妈呀" in received or "天" in received:
            print "不要吃惊,我就是如此聪慧!!!嘟嘟嘟"
        else:
            res = "宝宝不明白你在说什么???"

chat = chat_robot()
next(chat)    #开始执行

while True:
    c = raw_input(">>:")
    if not c:
        continue
    elif c.lower() == "q":
        print "byebye ,see you next time!!!lalala"
        break
    else:
    #send方法将用户输入传给yield,根据用户输入作出判断,将返回值赋给response
        response = chat.send(c)  
    #打印response
        print response

运行效果:

>>:你好呀,小机器人
你好呀!!!欢迎来到yueer的世界

>>:你是男孩还是女孩呀
本姑娘还小,没有谈过恋爱!!!傲娇脸

>>:我的妈呀,自恋
不要吃惊,我就是如此聪慧!!!嘟嘟嘟

>>:聪慧吗,笨猪猪
请文明用语哦!!!我会生气的,哼~~~

>>:好啊,你比较聪明~
宝宝不明白你在说什么???
>>:q
byebye ,see you next time!!!lalala
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值