生成器 經過列表生成式,咱們能夠直接建立一個列表。可是受內存限制,列表容量確定是有限的。 並且,建立一個不少不少元素的列表,不只佔用很大的內存空間,若是咱們僅僅訪問前幾 元素,那後面絕大多數的元素佔用的空間就白白浪費了。 因此,若是列表元素能夠按照某種算法推算出來,那多好!這樣就沒必要浪費空間了,這樣 你好我好你們好。 在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator 要建立一個generator,有不少種方法。 1)只要把一個列表生成式的[]改成(),就建立了一個generator。 L = [i * i for i in range(5)] print(L)#[0, 1, 4, 9, 16] g = (i * i for i in range(5)) print(g)#<generator object <genexpr> at 0x111136ca8> 建立L和g的區別僅在於最外層[]和(),L是一個list而g是一個genratot。 咱們能夠直接打印出list的每個元素,但咱們怎麼打印出generator的每個元素呢? 咱們能夠經過 next(g)挨個拿其中的元素,最後一個取完以後再進行 next()操做會 報錯(StopIteration)。 一個一個取是在太繁瑣,咱們能夠用for循環,由於genertor是一個可迭代對象。 g = (i * i for i in range(5)) for i in g: print(i) #用for循環來迭代,而且不須要關心StopIteration報錯。 2)用函數來實現。 好比,斐波那契數列:1,1,2,3,5,8,13,21,34.... 用列表生成式寫不出來,可是咱們能夠用函數把它打印出來: def fib(number): n, a, b = 0, 0, 1 while n < number: print(b) a, b = b, a + b n = n + 1 return 'OK!' print(fib(5)) 結果: 1 1 2 3 5 OK! 咱們能夠看出從第一個元素開始,推算出後續任意的元素。很像generator。 要把fib函數變成generator,只須要把 print(b)改成 yield b就能夠了: def fib(number): n, a, b = 0, 0, 1 while n < number: yield b a, b = b, a + b n = n + 1 return 'OK!' print(fib(5))#<generator object fib at 0x105606ca8> 注意: 這裏難理解的就是generator和函數的執行流程是不同的。 函數是順序執行,遇到return語句或者最後一行函數語句就 返回。 注意:函數建立一次生成一個生成器,因此咱們會將建立的生 成器賦值給一個變量。若是直接用函數自己這個生成器, 咱們沒用一次生成一個新的生成器對象,因此,咱們一 般都將建立的生成器賦給一個變量。 generetor的函數,在每次調用 next()的時候執行,遇到 yield語句返回,再次執行時從上次返回的yield語句處繼續 執行。 例子: def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5) f = odd() print(next(f)) print(next(f)) print(next(f)) 結果: step 1 1 step 2 3 step 3 5 執行三次後再執行 next(f) 就會報錯了。 能夠看到odd不是普通函數,而是generator遇到yield就會中斷 下次又繼續執行,執行三次後已經沒有yield能夠執行,因此再執 行 next(f) 就會報錯了。最後的yield後面通常不寫東西。 把函數改成generator後,咱們基本不這麼用 next()來獲取下一個返回值 而是直接使用for循環來迭代。 好比上面的fib函數。 def fib(number): n, a, b = 0, 0, 1 while n < number: yield b a, b = b, a + b n = n + 1 return 'OK!' for i in fib(5): print(i) 結果: 1 1 2 3 5 注意:用for循環調用generator時,發現拿不到generator的return 語句的返回值。若是想要拿到返回值,必須捕獲StopIteration錯誤 返回值包含在StopIteration的value中。 (關於如何捕獲,異常處理見。) 接下來咱們來看send⽅方法, send和next()同樣均可以讓生成器執行到下一個yield. 例子; def eat(): print("我吃什麼啊") a = yield "饅頭" print("a=",a) b = yield "⼤大餅" print("b=",b) c = yield "韭菜盒⼦子" print("c=",c) yield "GAME OVER" gen = eat() # 獲取⽣生成器器 ret1 = next(gen) print(ret1) ret2 = gen.send("胡辣湯") print(ret2) ret3 = gen.send("狗糧") print(ret3) ret4 = gen.send("貓糧") print(ret4) 結果: 我吃什麼啊 饅頭 a= 胡辣湯 ⼤大餅 b= 狗糧 韭菜盒⼦子 c= 貓糧 GAME OVER send和next()區別: 1. send()和next()都是讓生成器向下走一次 2. send能夠給上一個yield的位置傳遞值,不能給最後一個yield發送值. 在第一次執行生成器代碼的時候不能使用send() 三道題: <1> def add(a, b): return a+b def Test(): for i in range(4): yield i g = Test() for n in [2, 10]: g = (add(n, i) for i in g) print(list(g)) 結果: [20, 21, 22, 23] 分析: print(list(g))這個語句以前都是準備階段,只程序走到 print(list(g))這個語句這裏程序纔會執行,那麼在此程 序執行的準備階段前面作了什麼事呢: 到for循環這 n = 2 g = (add(n, i) for i in Test()) n = 10 g = (add(n, i) for i in add(n, i) for i in Test()) 這個時候執行list(g)其實執行的只是n==10的時候 結果:[20, 21, 22, 23] <2> def func(): print('1') yield 'This is one step' print('2') yield 'This is two step' print('3') yield 'This is theree step' it = func()#函數返回生成器 print(list(it)) 結果: 1 2 3 ['This is one step', 'This is two step', 'This is theree step'] <3> def func(): print(111) yield 222 g = func() # 生成器g g1 = (i for i in g) # 生成器g1. 可是g1的數據來源於g g2 = (i for i in g1) # 生成器器g2. 來源g1 print(list(g)) # 獲取g中的數據. 這時func()纔會被執行. 打印111.獲取到222. g完畢. print(list(g1)) # 獲取g1中的數據. g1的數據來源是g. 可是g已經取完了了. g1 也就沒有數據了 print(list(g2)) # 和g1同理 結果: 111 [222] [] [] Form zero to hero