咱們先考慮一個場景:python
有個情景須要循環輸出1——10.算法
這裏給兩種方法:緩存
list1 = [1,2,3,4,5,6,7,8,9,10] for i in list1: print(i)
for i in range(1,11): print(i)
兩種方式輸出結果同樣,可是咱們考慮一下,若是要求輸出1——1000000呢?函數
第一種方式會致使list1裏面真實放入1000000長度的數字,佔用空間很大,明顯不是明智之舉,spa
再來看第二種方法,用到range幫助咱們生成數據,在python3中range的本質就是一個生成器。.net
在python2中:range返回的是一個等差列表,好比[0,1,2,3,4,5,6,7,```````], 而xrange纔是返回一個生成器對象. 即python2 range()==[```````````````````], python2 xrange()==python3 range()code
具體對比查看:https://blog.csdn.net/humanking7/article/details/45950967對象
(一)這裏寫一個函數,在生成器函數的名稱中加上gen 前綴或後綴,不過這不是必要的習慣:blog
1 def gen_create_range(start,end): 2 3 while start < end: 4 yield start 5 start += 1 6 7 for i in gen_create_range(1,5): 8 print(i)
#output:generator
1
2
3
4
這個函數沒有return 可是能夠有返回值,注意看裏面有個yield關鍵字,這個函數和range()函數很像。
(二)什麼是生成器:函數定義中包含yield關鍵字那麼函數就變成了生成器。
概念:若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator
生成器表達式: 通列表解析語法,只不過把列表解析的[]換成()
g= (x**2 for x in range(5)) print g >> <generator object <genexpr> at 0x0000000002771798> #若是、 L=[x**2 for x in range(5)] print L >> [0, 1, 4, 9, 16]
也就是說:建立L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator
1).函數中只要出現了yield語句就會將其轉變成一個生成器函數
特別之處在於,yield 的做用就是把一個函數變成一個 generator,帶有 yield 的函數再也不是一個普通函數,Python 解釋器會將其視爲一個 generator
與普通函數不同,生成器值會在迭代操做的時候才能運行.yiled能夠把函數中斷,保存狀態和繼續執行的能力
比如一個武打片裏面的慢鏡頭回放,yield把函數裏面你要保存的值中斷並保存,你經過調用next()來回放
好比:
def countdown(n): print('Starting to count from',n) while n>0: yield n n-=1 print('done') c=countdown(3) print(c) >> <generator object countdown at 0x0000000002821828> #表示這是一個生成器
2).調用該generator時,首先要生成一個generator對象,而後用next()函數不斷得到下一個返回值
好比:
c=countdown(3) #run the first yield and emit a value print(next(c)) >> Starting to count from 3 3 #run the next yield print(next(c)) >> 2 #run the next yield print(next(c)) >> 1 #run the next yield print(next(c)) >> done print next(c) StopIteration
深刻解釋:
你把yield想象成時間斷點,運行一次next就回放一下,看起來就好像一個函數在正常執行的過程當中被 yield 中斷了數次,每次中斷都會經過 yield 返回當前的迭代值
第一次next()是打印了 print('Starting to count from',n),提取第一次的保存值是3
第二次再運行next()是繼續在while裏面的斷點接着走,因此沒有打印print('Starting to count from',n), 而是直接提取第二次的保存值2
第三次再運行next()是繼續在while裏面的斷點接着走,因此直接輸出1
第四次再運行next()的時候,發現yield緩存的武打片慢鏡頭都已經放完了,因此輸出done以後,報了個錯StopIteration
3).生成器函數用for循環
for n in countdown(6): print(n) >> Starting to count from 6 6 5 4 3 2 1 done
正確的方法是使用for循環,由於generator也是可迭代對象
4yield 與 return相愛相殺
1).在一個生成器中,若是沒有return,則默認執行到函數完畢時返回StopIteration
def gen1(): yield 100 g1=gen1() print(next(g1)) >>100
第一次調用next(g1)時,會在執行完yield語句後掛起,因此此時程序並無執行結束。
print(next(g1)) >> Traceback (most recent call last): File "C:/about_gen.py", line 71, in <module> print(next(g1)) StopIteration
程序試圖從yield語句的下一條語句開始執行,發現已經到告終尾,因此拋出StopIteration異常
2).若是遇到return,若是在執行過程當中 return,則直接拋出 StopIteration 終止迭代
def gen2(): yield 200 return yield 300 g2=gen2() print(next(g2)) >>200 # 程序停留在執行完yield 200語句後的位置 print(next(g2)) >> File "C:/about_gen.py", line 82, in <module> print(next(g2)) StopIteration
程序發現下一條語句是return,因此拋出StopIteration異常,這樣yield 'b'語句永遠也不會執行
生成器這個概念一開始很難理解,有點古怪,可是時間久了才知道他的妙用
另外生成器函數是沒有辦法使用return來返回值