python 生成器generator

  

關於生成器,主要有如下幾個 關鍵點的內容python

1、什麼是generator ,爲何要有generator?數組

2、兩種建立生成器方式數據結構

3、yield關鍵字函數

4、generator 兩個調用方法 next() 和send()spa

 

 

1、什麼是generator ,爲何要有generator?操作系統

generator 是一種方式 ,是一種藉助於循環,而後不斷產生值的一種機制。(我是能理解機制這種描述,想了個例子來講明這個例子感受又不對。)協程

那麼爲何須要generator呢?1、 當你須要獲取大量值的時候,咱們能夠將值存在列表裏面,而後循環,讀出沒一個值。這沒毛病。對象

可是你知道數組這種數據結構,他是將全部的值都緊挨着放在一塊的,blog

當咱們將這不少值,都放在內存中,主存是須要申請很大一塊空間,內存

進行存放的,而咱們的操做系統還要執行其餘的任務,也要用到主存。這樣將會致使操做RAM大量被佔用,其餘任何執行不了,或者乾脆,其餘任務執行完了,把地方讓出來給你,你再去執行。以上的描述你們能感受出來效率是很是低的。

但是咱們有的時候就是須要啊,須要獲取這種大量數據。(什麼狀況下,其實我寫了幾個項目如今也沒有用到過generator,不過知道python的協程就是用生成器實現的。)而生成器他經過循環,計算出的數據,也就是說每循環一次就產生一次數據。這樣是不須要向上面同樣大量佔用內存空間的,至於你說你須要計算啊(對於cpu來你這點代碼量,簡直就是連9牛一毛都稱不上,這大兄弟但是每秒鐘,能計算幾十億次)。

2、兩種生成器的建立方式

1 、(x for x in range) ,就是將列表推到值外邊的列表換成了元組。

二、函數中包含yield 關鍵字

def f1(n):
    yield n
obj = f1(1)
print(obj)



返回值是這個東西 <generator object f1 at 0x0000000001F1FFC0> 
是一個生成器對象

  

三 、 yield 關鍵字

咱們知道一個函數包含一個yield關鍵字 ,返回的就是一個generator ,那麼這個yield關字是怎麼運做的,他都有那些特性

請看以下代碼,這個就是一個生產這消費者模型

def consumer():
    r = 'ok'
    while True:
        n1 = yield r
        while not n1:
            print('若是走了這裏,說明,下一次調用以前先將上一次的NONE給賦值了')
        print('consumer consume %s'%(n1))
        r = '我不想吃包子,我想吃pizza'
def producer(c):
    data =c.send(None)
    print(data ,'到底一次yield有沒有返回值')
    n=0
    while n <5:
        n+=1
        print('生產者生產了%s'%n)
        data_2=c.send(n)
        print('消費者其實想的是%s'%(data_2))
obj =consumer()
producer(obj)

  

我建議,若是你看了個人博客,能夠複製一下代碼去執行一下。就能總結出來yield關鍵字有以下特性:

一、yiled 關鍵字相似於return ,就是函數執行到這裏後,就不在向下執行,而後返還一個返回值。可是(一提可是就要仔細看啊)當 執行到yield 關鍵字時,整個函數一個運行狀態是還保存在內存,當下次再調用這個生成器時,會從yield開始,在向下執行,

而不是從函數開頭重新執行一遍。而後循環到了yield 在卡住,等待下次再調用

二、yield 能夠被傳值,不過必需要經過send()方法

 

4、generator 兩個調用方法 next() 和send()

咱們一直在強調,generator 是藉助於循環,不斷產生新的值,因此就能理解 ,通常一個生成器都應該是一個循環中被調用,產生一個新的值。

因此

 

def f1(n):
    while True:
        n +=1
        yield n
obj = f1(1)

for i in obj:
    print(i)


你可嘗試一下,無限循環的感受。

  

固然通常狀況下,不會是無限循環 ,確定是有一個終止條件的。

以下

def f1(n):
    while n<5:
        n +=1
        yield n
obj = f1(1)


j =0
while j<5:
    j+=1
    try:
        print(next(obj))
    except StopIteration as e:
        print('別調用了,最多能計算這麼多')

當生成器已經計算結束後,你在去調用會報錯的 ,若是是直接用for 調用是不會報錯的。其實for 結構裏面就相似上面的代碼,只是當循環完generator後,抓去了對應的異常。結束循環。

  

最後則是send 方法 , 而send 方法,他有兩個做用,第一個是能夠調用這個generator計算,第二個就是給yield賦值,在將上面的代碼賦值一下

def consumer():
    r = 'ok'
    while True:
        n1 = yield r
        while not n1:
            print('若是走了這裏,說明,下一次調用以前先將上一次的NONE給賦值了')
        print('consumer consume %s'%(n1))
        r = '我不想吃包子,我想吃pizza'
def producer(c):
    data =c.send(None)
    print(data ,'到底一次yield有沒有返回值')
    n=0
    while n <5:
        n+=1
        print('生產者生產了%s'%n)
        data_2=c.send(n)
        print('消費者其實想的是%s'%(data_2))
obj =consumer()
producer(obj)

  關於send 方法有一個特性,就是在第一次啓動生成器的時候,要傳一個None,或者先用next調用一下。緣由是,當第一次走到yield的時候,yield 直接將會返回值返回,而後此次執行就停掉了,並無發生賦值的操做,因此你傳進來一個值,是沒有效果的因此python的源碼裏面就作了處理。而第二次在在用send喚醒generaot ,send方法在傳一個值,在將這個值傳給yield 關鍵字。而後yield在賦值,而後整個函數再向下運行。

 

還有個小點,就是在函數調用,生成一個generatro對象時,函數是什麼執行的,只有在被調用,或者被for循環時,纔開始執行。

相關文章
相關標籤/搜索