關於生成器,主要有如下幾個 關鍵點的內容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循環時,纔開始執行。