當列表包含很是多元素時,會佔用大量存儲空間,而若是僅需訪問前面幾個元素,則後面絕大多數元素佔用的空間都被浪費了
若是列表元素能夠按照某種算法推算出來,則能夠在循環的過程當中不斷推算出後續的元素,這樣就沒必要建立完整的list,從而節省大量的空間
在Python中,這種一邊循環一邊計算的機制,稱爲生成器(generator)
經過定義可知,generator保存的不是數據,而是得到元素的算法
建立生成器有兩種方式
1) 簡單地把列表生成式改爲generator,就是建立列表時,將[]修改成()
2) 經過函數實現複雜邏輯的generator,函數體內使用yield語句
算法
生成器工做原理
當使用for循環時,在for循環的過程當中不斷計算出下一個元素,並在適當的條件結束for循環
當使用next()時,調用一次next,生成器計算出下一個元素,只到生成器沒法計算出下一個值並報Iteration錯誤函數
生成器與普通函數區別
普通函數:每調用一次,都會從函數體第一行代碼開始執行,直到最後遇到return語句或最後一行,返回結果
生成器函數:每調用一次,第一次執行會從函數第一行代碼開始執行,遇到yield語句返回,後續代碼不會執行
當再次調用時,會接着上次執行的yield語句後開始執行,直到遇到下一個yield語句返回,後續代碼不會執行
以此類推,直到遇到最後一個yield語句並返回
此時,若是使用的是for循環迭代的,則生成器正常結束,若是使用的是next()迭代的,會報錯
實際上生成器返回的是一個generator對象spa
使用示例:
建立列表方式建立生成器code
L = [x * x for x in range(10)] g = (x * x for x in range(10)) #建立列表和生成器的區別僅在於最外層的[]和(),L是一個list,而g是一個generator
next()迭代生成器對象
next(g) #輸出:0 next(g) #輸出:1 next(g) #輸出:4 next(g) #輸出:9 ... next(g) #輸出:StopIteration, #generator保存的是算法,每次調用next(g),generator就計算出下一個元素的值,直到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤
for循環迭代生成器blog
g = (x * x for x in range(10)) for n in g: print(n) #for循環體內沒有調用next方法,所以不用擔憂報StopIteration錯誤 #函數變成generator後,基本上不會用next()來獲取下一個返回值,而是直接使用for循環來迭代
函數實現生成器
若是一個函數定義中包含yield關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generatorgenerator
def odd(): #odd不是普通函數,而是generator yield 1 yield 3 yield 5 o = odd() #調用該generator時,首先要生成一個generator對象,而後用next()函數不斷得到下一個返回值 next(o) #輸出:1,第1次調用next,執行odd()函數的yield 1並返回1,後面的yield 3和yield 5不執行 next(o) #輸出:2,第2次調用next,直接執行odd()函數的yield 3,後面的yield 5不執行,並返回2 next(o) #輸出:3,第3次調用next,直接執行odd()函數的yield 5,並返回5 next(o) #輸出:報StopIteration錯誤,第4次調用next,發現odd()函數沒有其餘yield值,並報StopIteration錯誤
斐波那契數列實現
生成器函數定義io
def fib(max): n, a, b = 0, 0, 1 while n < max: #循環過程當中不斷調用yield,就會不斷中斷。固然要給循環設置一個條件來退出循環,否則就會產生一個無限數列出來 yield b a, b = b, a + b n = n + 1 return 'done'
迭代斐波那契數列for循環
for n in fib(6): print(n) #用for循環調用generator時,是沒法獲得generator的return語句返回值的,由於生成器只能獲得yield值 #輸出: 1 # 1 # 2 # 3 # 5 # 8
迭代斐波那契數列,同時輸出生成器的return值class
g = fib(6) while True: try: x = next(g) print('g:', x) except StopIteration as e: print('Generator return value:', e.value) #想要拿到return語句返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中 break #輸出: g: 1 # g: 1 # g: 2 # g: 3 # g: 5 # g: 8 # Generator return value: done