1. 生成器定義
一邊循環一邊計算的機制,稱爲:生成器(generator)
生成器(generator)是構造新的可迭代對象的一種簡單方式。
一般的函數執行之後只會返回單個值,而生成器則是以延遲的方式返回一個值序列,
即每返回一個值之後暫停,直到下一個值被請求時再繼續。
生成器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解生成器就是一個迭代器
2. 爲什麼要有生成器
python生成器的優點:
(1)延遲計算,一次返回一個結果。生成器不會一次生成所有的結果,而是一邊循環一邊計算,這對於大數據量處理,是個非常有用的優勢。因爲在編程的實際應用中,佔用內存量是工程師必須考慮的一個問題。
列表所有數據都在內存中,如果有海量數據的話將會非常耗內存。
如:僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
如果列表元素按照某種算法推算出來,那我們就可以在循環的過程中不斷推算出後續的元素,這樣就不必創建完整的list,從而節省大量的空間。
簡單一句話:我又想要得到龐大的數據,又想讓它佔用空間少,那就用生成器!
(2)有效提高代碼可讀性。使用生成器以後,代碼行數更少。
3.如何創建生成器
第一種方法很簡單,只要把一個列表生成式的[]
改成()
,就創建了一個generator:
In [189]: gen = (x ** 2 for x in range(100))
In [190]: gen
Out[190]: <generator object <genexpr> at 0x1022ef630>
創建L
和g
的區別僅在於最外層的[]
和()
,L
是一個list,而g
是一個generator。
方法二, 如果一個函數中包含yield
關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator。調用函數就是創建了一個生成器(generator)對象。要創建一個生成器,只需將函數中的return替換爲yeild即可:
def _make_gen():
for x in range(100):
yield x ** 2
gen = _make_gen()
生成器表達式也可以取代列表推導式,作爲函數參數:
In [191]: sum(x ** 2 for x in range(100))
Out[191]: 328350
In [192]: dict((i, i **2) for i in range(5))
Out[192]: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
4. 生成器的工作原理
(1)生成器(generator)能夠迭代的關鍵是它有一個next()方法,
工作原理就是通過重複調用next()方法,直到捕獲一個異常。
(2)帶有 yield 的函數不再是一個普通函數,而是一個生成器generator。
可用next()調用生成器對象來取值。next 兩種方式 t.__next__() | next(t)。
可用for 循環獲取返回值(每執行一次,取生成器裏面一個值)
(基本上不會用next()
來獲取下一個返回值,而是直接使用for
循環來迭代)。
(3)yield相當於 return 返回一個值,並且記住這個返回的位置,下次迭代時,代碼從yield的下一條語句開始執行。
(4).send() 和next()一樣,都能讓生成器繼續往下走一步(下次遇到yield停),但send()能傳一個值,這個值作爲yield表達式整體的結果
通過例子來理解下:
In [63]: def yield_test(n):
(1)...: print("begin:")
(2)...: for i in range(n):
(3)...: print('yield_1_i=',i)
(4)...: yield call(i)
(5)...: print('yield_2_i=',i)
(6)...: print("Done")
...:
In [64]: def call(i):
(7)...: print('call',i)
(8)...: return i*2
...:
In [65]: for i in yield_test(3):
(9)...: print("now:",i)
yield相當於 return 返回一個值,並且記住這個返回的位置,
下次迭代時,代碼從yield的下一條語句開始執行。
結果:
begin (運行9)
yield_1_i=0 (進入第一輪for循環,i=0,運行3)
call 0 (運行4——7——8,得到0*2=0,返回0)
now 0 (yield相當於return返回一個值,並且記住這個返回的位置,所以運行9)
yield_2_i=0 (下次迭代時,代碼從yield的下一條語句開始執行,所以運行5,i不變仍然是0)
yield_1_i=1 (進入第二輪for循環,i=1,運行3)
call 1 (運行4——7——8,得到1*2=2,返回2)
now 2 (yield相當於return返回一個值,並且記住這個返回的位置,所以運行9)
yield_2_i=1 (下次迭代時,代碼從yield的下一條語句開始執行,所以運行5,i仍是1)
yield_1_i=2 (進入第三輪for循環,i=2,運行3)
call 2 (運行4——7——8,得到2*2=4,返回4)
now 4 (yield相當於return返回一個值,並且記住這個返回的位置,所以運行9)
yield_2_i=2 (下次迭代時,代碼從yield的下一條語句開始執行,所以運行5,i仍是2)
done (循環結束,運行6)
總結:
什麼是生成器?
生成器僅僅保存了一套生成數值的算法,並且沒有讓這個算法現在就開始執行,而是我什麼時候調它,它什麼時候開始計算一個新的值,並給你返回。