學習生成器之前,首先需要認識列表生成式,直奔主題。 1、簡單列表生成式示例: 1 b = [ i for i in range(10)] 2 print(b) 3 4 >>> 5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 看樣子好像很NB的樣子,其實它等價於: 1 c = [] ...
學習生成器之前,首先需要認識列表生成式,直奔主題。
1、簡單列表生成式示例:
1 b = [ i for i in range(10)] 2 print(b) 3 4 >>> 5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
看樣子好像很NB的樣子,其實它等價於:
1 c = [] 2 for i in range(10): 3 c.append(i) 4 print(c) 5 6 >>> 7 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
其實還是有點NB的,畢竟代碼少了,手動嘻嘻嘻!
2、削微高級點的列表生成式:
1 def func(x): 2 a = x + 1 3 b = a + x 4 return b 5 d = [ func(i) for i in range(10) ] 6 print(d) 7 8 >>> 9 [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
生成式生成的數據可以交給函數處理,進而得到預定規則的一組數據。
下麵有請主角登場!!!!
生成器:在Python中,一邊迴圈一邊計算的機制,稱為生成器:generator。
我們知道了,通過列表生成式,我們可以直接創建一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。
創建生成器的姿勢:將生成式的"[]"換成"()"就ok了,是不是超級簡單。。。
1 a = ( i for i in range(10)) 2 print(a) 3 for i in a: 4 print(i) 5 6 >>> 7 <generator object <genexpr> at 0x00000270A14EB0B0> 8 0 9 1 10 .. 11 9
由上面的例子可見,列印的a已不是一個列表,而是a的記憶體地址。而且a可以通過for迴圈將其中的值取出。
就用一個小慄子完成生成式和生成器比較吧:
1 # 生成b需要很長時間 2 b = [ i for i in range(100000000) ] 3 # 生成c很快,因為不需要實際寫數據,c的數據是在使用時才生成 4 c = ( i for i in range(100) )
生成器特性:
1.生成器只能一個個的取數據
2.生成器只有在調用時才會生成相應的數據
3.生成器只記住當前位置的地址,之前的的記憶體地址都沒了
4.只有一個__next__()方法
5.生成器不能根據索引位置取值
1 c = ( i for i in range(100) ) 2 print(c.__next__()) 3 print(c.__next__()) 4 print(c[0]) 5 6 >>> 7 0 8 1 9 TypeError: 'generator' object is not subscriptable
#############################################################
示例:瞭解斐波那契數列(下個數為前兩個數的和)
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a+b #註意a, b = b, a+b 等價於 t(b,a+b);a=t[0];b=t[1],而不是 a=b;b=a+b 6 n = n + 1 7 return "done" 8 fib(7) 9 >>> 10 1 11 1 12 2 13 3 14 5 15 8 16 13
使斐波那契數列變成一個生成器
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 yield b #yield返回當前狀態的值,程式停在此處 5 a, b = b, a+b 6 n = n + 1 7 return "---done---" 8 # fib(5) #此時執行,並無返回值,因為沒有取值。使用__next__()進行取值 9 f = fib(5) 10 print(f.__next__()) 11 print(f.__next__()) 12 print(f.__next__()) 13 print(f.__next__()) 14 print(f.__next__()) 15 print(f.__next__()) 16 17 >>> 18 1 19 1 20 2 21 3 22 5 23 StopIteration: ---done--- # 當超出時報錯
使用yield後,可以使用__next__()方法,中斷函數的執行,在中斷之後可以執行其他的操作,然後還可以通過__next__()再進入
例如:
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 # print(b) 5 yield b #yield返回當前狀態的值,程式停在此處 6 a, b = b, a+b 7 n = n + 1 8 return "---done---" 9 f = fib(5) 10 f.__next__() 11 X = f.__next__() 12 print(X) 13 print("休息一會兒,馬上就回來了") 14 Y = f.__next__() 15 print(Y) 16 print("又走了...") 17 Z = f.__next__() 18 print(Z) 19 >>> 20 1 21 休息一會兒,馬上就回來了 22 2 23 又走了... 24 3
能看到這,那你又又又又又又要學到了:抓異常、抓異常、抓異常。重要的事情說三遍!!!
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 # print(b) 5 yield b #yield返回當前狀態的值,程式停在此處 6 a, b = b, a+b 7 n = n + 1 8 return "---done---" 9 g = fib(8) 10 while True: 11 try: 12 x = next(g) # 等價於 x = g.__next__() 13 print("value:", x) 14 except StopIteration as e: #當出現StopIteration時執行下麵的代碼 15 print("Generator return value", e.value) 16 break 17 18 >>> 19 value: 1 20 value: 1 21 value: 2 22 value: 3 23 value: 5 24 value: 8 25 value: 13 26 value: 21 27 Generator return value ---done---
生成器示例高級,實現並行:
import time def consum(name): print("%s 要來吃包子了" % (name)) while True: baozi = yield print("%s個包子被%s吃了" % (baozi, name)) def product(name): d = consum("flb") d2 = consum("wxl") d.__next__() # next喚醒yield,不會給yield傳值 d2.__next__() print("%s要來做包子了" %(name)) for i in range(3): time.sleep(1) print("%s做了%s個包子" %(name,i)) d.send(i) # send喚醒yield,會給yield傳值 d2.send(i) product("TJ") >>> flb 要來吃包子了 wxl 要來吃包子了 TJ要來做包子了 TJ做了0個包子 0個包子被flb吃了 0個包子被wxl吃了 TJ做了1個包子 1個包子被flb吃了 1個包子被wxl吃了 TJ做了2個包子 2個包子被flb吃了 2個包子被wxl吃了
END!!!