生成器可以理解為一種的數據結構,將演算法保存,每次計算並返回一個結果,實現了迭代器協議,生成器也是迭代器 生成器有兩種表現形式,1)生成器表達式;2)生成器函數 1、生成器表達式 說到生成器表達式,就得先說一下列表推導式 [i for i in range(10)] ,生成器表達式,就是將 [ ] 改 ...
生成器可以理解為一種的數據結構,將演算法保存,每次計算並返回一個結果,實現了迭代器協議,生成器也是迭代器
生成器有兩種表現形式,1)生成器表達式;2)生成器函數
1、生成器表達式
說到生成器表達式,就得先說一下列表推導式 [i for i in range(10)] ,生成器表達式,就是將 [ ] 改為 (),區別如下所示
1 >>> b = [i for i in range(20)] 2 >>> b 3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 4 5 >>> b = (i for i in range(20000)) 6 >>> b 7 <generator object <genexpr> at 0x00000185EC6104F8> 8 >>> next(b) 9 0 10 >>> next(b) 11 1 12 >>> next(b) 13 2
生成器表達式優點:省記憶體,一次只計算返回一結果。 缺點:不知道有幾個元素,只能往後遍歷,不能向前遍歷,且只能整個生成器只能遍歷一次
列表推導式優點:可以通過下標獲取元素。 缺點:占用記憶體大
2、生成器函數
生成器函數:在函數中如果出現了yield關鍵字,那麼該函數就不再是普通函數,而是生成器函數
我們都知道 return 是函數的返回值,yield 也可以將值返回,我們先看一個簡單的生成器函數與普通函數之間的差別
1 >>> def test(): 2 ... print("_______生成器函數") 3 ... yield "返回值1" 4 ... yield "返回值2" 5 ... yield "返回值3" 6 ... 7 >>> test() 8 <generator object test at 0x000001651BEC0570> 9 >>> t1 = test() 10 >>> print(t1.__next__()) 11 _______生成器函數 12 返回值1 13 >>> print(t1.__next__()) 14 返回值2 15 >>> print(t1.__next__()) 16 返回值3 17 >>> print(t1.__next__()) 18 Traceback (most recent call last): 19 File "<stdin>", line 1, in <module> 20 StopIteration
可以看到在第7行調用test()函數的時候,並沒有執行函數,而是返回了一個生成器對象。在第10行執行 t1.__next__() 才真正的執行了函數,返回 "返回值1",並保存當前的生成器函數的狀態。
每執行一次 next(),生成器函數將執行到下一 yield 並將相應的結果返回,直到產生StopIteration異常。
理解了上述過程,就不難理解生成器表達式的原理,next(t1) 等價於 t1.__next__()
def test(): for i in range(10): yield i print(test()) t2 = test() print(next(t2)) print(next(t2)) print(next(t2)) t3 = (i for i in range(10)) print(t3) print(next(t3)) print(next(t3)) print(next(t3)) #輸出結果 <generator object test at 0x000001A12D8FF570> 0 1 2 <generator object <genexpr> at 0x000001A12D8FF5E8> 0 1 2
3、生成器強調,send()
生成器中還有一個重要的方法 send() 方法,send() 方法可以解決將值傳遞給生成器函數的問題,具體如下:
1 >>> def test(): 2 ... print("____start_____") 3 ... y1 = yield "返回1" 4 ... print("____生成器函數內:%s" % y1) 5 ... y2 = yield "返回2" 6 ... print("____生成器函數內:%s" % y2) 7 ... yield "返回3" 8 ... 9 >>> 10 >>> t1 = test() 11 >>> print(t1.__next__()) 12 ____start_____ 13 返回1 14 >>> print(t1.send("a")) 15 ____生成器函數內:a 16 返回2 17 >>> print(t1.send("b")) 18 ____生成器函數內:b 19 返回3
在這裡第一次獲取值的時候,不能用send(),否者會報錯,如果一定要用send,則傳值None,如下:
>>> t2 = test() >>> t2.send("a") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't send non-None value to a just-started generator >>> t3 = test() >>> t3.send(None) ____start_____ '返回1'
對於send() 不是很理解的可以嘗試往下看,下邊是我的一些個人理解,不是很準確,我覺得這樣會比較好理解一些
我們可以把函數內 第3行 y1 = yield "返回1" 看成兩個過程:1)yield "返回1" 2)y1 = receive_from_send() [並沒有這個方法,只是為了理解說明]
這樣就可以看到,當執行 send() 時將參數"a" 傳遞給生成器函數,併在 1)過程停留,傳遞過去的值沒有語句接收,則會出錯
所以第一次是不能傳值給生成器函數,y1接收到的值為下一次遍歷遍歷生成器接收的值。
這裡也可以也可以在,上上邊的代碼執行流程可以看出,執行了 14行,y1才會接收到值。
補充一點:每執行一次 1)next(),2)__next(),3)send(),都會在yield語句停留,並保存當前狀態,知道直到產生StopIteration異常,結束遍歷。
通過生成器,實現斐波那契數列
def fib(n): a, b = 0, 1 while True: yield b a, b = b, a + b if b > n: return f1 = fib(100) #小於 100 的斐波那契數列 for i in f1: print(i,end=" ") #輸出結果 1 1 2 3 5 8 13 21 34 55 89