高級特性之生成器

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章