列表生成式
通過上一篇介紹 列表生成式文章可以知道,它可以快速建立我們需要的列表
侷限性
- 受記憶體限制,列表生成式建立的列表的容量肯定有限的
- 不僅佔用很大的儲存空間,如果我們僅僅需要訪問前幾個元素,那後面絕大多數元素佔用的空間都白白浪費了
什麼是生成器
- 若列表元素可以按照某種演算法算出來,就可以在迴圈的過程中不斷推算出後續需要用的元素,而不必建立完整的 list,從而節省大量的空間
- 邊迴圈邊計算的機制,叫生成器(generator)
最簡單的生成器
L = [x * x for x in range(10)] print(L) print(type(L)) L = (x * x for x in range(10)) print(L) print(type(L)) # 輸出結果 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <class 'list'> <generator object <genexpr> at 0x000001D607541EB8> <class 'generator'>
只要把一個列表生成式的 [] 改成 () ,就建立了一個 generator
如何列印生成器每個元素
直接簡單 for 迴圈
L2 = (x * x for x in range(10)) for i in L2: print(i)
next() 方法
可以獲取 generator 的下一個元素
基本不會使用這個
L2 = (x for x in range(10)) print(next(L2)) print(next(L2)) print(next(L2)) print(next(L2)) print(next(L2)) print(next(L2)) # 輸出結果 0 1 2 3 4 5
還有另一個方法 .__next()__
L2 = (x for x in range(10)) print(L2.__next__()) print(L2.__next__()) print(L2.__next__()) print(L2.__next__()) print(L2.__next__()) print(L2.__next__()) # 輸出結果 0 1 2 3 4 5
生成器的迭代原理
generator 能夠迭代的關鍵就是 next() 方法,通過重複呼叫 next() 方法,直到捕獲一個異常
yield 函式
- 帶有 yield 的函式不再是一個普通函式,而是一個生成器 generator
- yield 相當於 return 返回一個值,並且記住這個返回值的位置,下次迭代時,程式碼會從 yield 的下一條語句開始執行,直到函式結束或遇到下一個 yield
普通的斐波拉契數列
1, 1, 2, 3, 5, 8, 13, 21, 34, ...,除第一個和第二個數外,任意一個數都可由前兩個數相加得到
# 斐波拉契數列 def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 fib(8) # 輸出結果 1 1 2 3 5 8 13 21
它和生成器很像,知道第一個元素值,就可以推算後面的任意個元素了
是用 yield 的斐波拉契數列
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 fib(8) print(fib(8)) # 輸出結果 1 1 2 3 5 8 13 21 <generator object fib at 0x00000246A5001EB8>
生成器的執行流程
函式是順序執行,遇到 return 或者最後一行執行完就返回
而生成器的執行流程是
- 每次呼叫 next() 或 for 迴圈的時候執行,遇到 yield 就返回
- 一個生成器裡面可以有多個 yield
- 再次執行時從上次返回的 yield 語句處繼續執行
# 執行流程 def odd(): print('step 1') yield 1 print('step 2') yield 3 print('step 3') yield 5 L = odd() for i in L: print(i) # 輸出結果 step 1 1 step 2 3 step 3 5
生成器的工作原理
- 它是在 for 迴圈過程中不斷計算下一個元素,並在適當的條件結束 for 迴圈
- 對於函式改成的 generator 來說,,遇到 return 語句或者執行到函式最後一行時,就是結束 generator 的指令,for 迴圈隨之結束
生成器的優點
在不犧牲過多速度情況下,釋放了記憶體,支援大資料量的操作
不使用生成器下的程式碼
from tqdm import tqdm a = [] for i in tqdm(range(10000000)): temp = ['你好'] * 2000 a.append(temp) for ele in a: continue
執行結果
可以看到開始執行大資料量迴圈程式碼後,記憶體暴增,並且佔滿了電腦所有記憶體,很明顯這是不合理且不可接受的!
使用生成器的程式碼
def test(): for i in tqdm(range(10000000)): temp = ['你好'] * 2000 yield temp a = test() for ele in a: continue
執行結果
記憶體絲滑的很,奈斯!
生成器的應用場景
當然就是需要處理大資料量的場景了,比如一個檔案有幾百萬行資料,或者有幾百萬個檔案需要分別讀取處理