Python進階系列連載(5)——生成器(上)
生成器
還記得在迭代器裡我們說為什麼將列表轉為迭代器麼?
小明:因為列表太大的話佔用記憶體太大,做成迭代器可以節省空間,用的時候再拿出部分
是的,今天要講的生成器是不會把結果儲存在一個系列中,而是儲存生成器的狀態。
在每次進行迭代時返回一個值,直到遇到StopIteration異常結束。
見過這種東西吧:
你可以認為每一杯飲料就是一個生成的物件,我不會一次倒出所有的飲料
而是要喝的時候去倒出一杯(也就是需要的時候生成一個)
簡單方法建立生成器
我們看個例子:
我們發現,當要生成的list非常大時,丟擲異常,儲存報錯。
那怎樣生成這種巨大的list呢?
你親手試一下,發現瞬間程式就執行結束了
我們看到,b是一個generator,也就是生成器模式
你應該已經注意到,生成器的建立很簡單,將列表生成式的中括號改成小括號即可
注意:這裡說的不是列表,因為列表的中括號改成小括號是元組!
那我們怎麼生成一個內容呢?
和之前的迭代器相同,使用next()函式即可:
直到最後會丟擲異常,也就是到達了生成器的末端了
函式進化為生成器
還記得函式的定義麼?
我們在之前用遞迴定義了一個斐波那契數列
現在我們定義一個新的函式來生成斐波那契數列的第n項
為了實現後一項等於前兩項之和使用了a,b = b,a+b
為什麼這樣寫,留給大家思考~
提示:可以輸入n=3,自己感受一下呼叫函式過程中a和b的變化
值得注意的是,這個函式,當n=0時返回的是1,而不是正確的0
所以我們對其進行修改:
在迴圈之前,加了一個判斷
小明:老溼!你這個說的還是函式啊,和生成器有啥關係?說好的函式進化成生成器呢?
好的,我們看看函式怎麼進化為生成器!
我們把函式中的return換成yield
函式就進化成了生成器,當我們呼叫時,發現返回的是生成器物件
為了拿到資料,我們應該怎麼做呢?
小紅:老師,是不是可以試試next()函式呢?
對,不過在此之前,我們先要用一個變數去接收這個生成器物件
並且為了觀察生成器的特點,我們對函式進行修改!
仔細看好:
當我們使用next(a)對生成器操作一次時,會返回迴圈一次的值
也就是在yield處結束本次執行
但它的特點就是下次使用next(a)時,接著上次的斷點繼續執行,直到下一個yield
不斷使用next(a),直到執行到生成器結尾處,如下圖:
可能你對他的執行過程還不是特別清晰
我們加上print輸出來徹底搞懂他的執行過程:
發現每次返回值都是在yield的地方了吧~
總結
0.講了兩種生成器建立方式
1.加了yield的函式就變成了生成器
2.要定義一個變數接收生成器的返回值
3.使用next()獲取生成器每次返回的值,並且斷點在yield處
4.下次使用next()從上次的斷電往下執行,直到生成器末端(這裡表現為迴圈結束)
5.生成器屬於迭代器,所以肯定是可迭代物件啦~