Python—生成器
# 生成器 # 通過列表生成式,我們可以直接創建一個列表 # 但是,受到記憶體限制,列表容量肯定是有限的 # 創建一個100萬個元素的列表,不僅佔用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了 # 如果列表元素可以按照某種演算法推算出來,那我們可以在循環的過程中不斷推算出後續的元素,這樣就不必創建完整的list,從而節省大量的空間 # Python中,這種一邊循環一邊計算的機制,稱為生成器:generator from collections import Iterable # 創建一個list L = [x * x for x in range(10)] print('L:', L) # 創建一個generator g = (x * x for x in range(10)) print('g:', g) # 創建L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator # 使用for列印generator的每一個元素,因為generator是可迭代對象 print('查看g是否可迭代:', isinstance(g, Iterable)) for n in g: print(n) # 用函數列印斐波那契數列(用列表生成式寫不出來) def fib(max): l = [] n, a, b = 0, 0, 1 while n < max: l.append(b) a, b = b, a + b n = n + 1 return l print('fib(10):', fib(10)) # 把fib函數變成generator,只需把print(b)改為yield b就可以了 def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 print('fib(20) for generator:', fib(10)) fibList = [] for e in fib(10): fibList.append(e) print('fibList:', fibList) # 如果一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator # generator和函數的執行順序不一樣 # 函數是順序執行,遇到return語句或者最後一行就返回 # 而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行 # 定義一個generator,依次返回數字1,3,5: def odd(): print('step 1') yield 1 print('step 2') yield 3 print('step 3') yield 5 # 在調用generator時,首先要生成一個generator對象,然後用next()函數不斷獲得下一個返回值 o = odd() print(next(o)) print(next(o)) print(next(o)) # StopIteration # print(next(o)) # 可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,下次又繼續執行 # 執行3次yield後,已經沒有yield可以執行了,所以,第4次調用next(o)就會報錯 # 回到fib的例子,我們在循環過程中不斷調用yield,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來 # 同樣的,把函數改成generator後,我們基本上從來不會用next來獲取下一個返回值,而是直接使用for循環來迭代