一文帶你探尋Python中的生成器
面試官: 聽說你熟悉python
,那么你能簡單闡述一下python
的裝飾器、生成器以及迭代器么?
我: emm, 我不清楚,我只是了解過python
最基本的代碼。
上述是弟弟前段時間去面試運維開發(fā),遇到的問題,emmm,運維是一個很雜的職業(yè),在小公司,總結(jié)一句話就是寬而淺,痛定思痛,決定來了解一下python
特性,于是乎,就有了這篇文章。
這邊文章,我們將介紹python
生成器,使用環(huán)境為: Python 3.6.8
如果你還沒有了解過迭代器,建議查看迭代器的文章:《python | 你知道for...in是底層原理是什么樣的么?探尋python迭代器》
什么是python生成器
上一篇文章我們介紹了迭代器,而生成器是一種特殊的迭代器,它內(nèi)部也有__iter__
方法和__next__
方法,在終止生成器的時候,還是會拋StopIteration
異常以此來退出循環(huán),只不過相比于迭代器,生成器還有特性會保存“中間值”,下次運行的時候,還會借助這個“中間值”來操作。生成器的關(guān)鍵字是yield
,我們下面來寫一個最簡單的生成器。
#!/usr/bin/env python def printNums(): i = 0 while i<10: yield i i = i + 1 def main(): for i in printNums(): print(i) if __name__ == '__main__': main()
粗看代碼,可能會覺著這個是個啥啊,為啥不直接用range
來生成,偏偏要用yield
,哎,不急,我們接著往下看為什么需要生成器,或者說,生成器解決了什么問題。
為什么需要python生成器
在說明這個問題之前,我們先來寫一個需求,輸出 0——10000000 以內(nèi)的數(shù)據(jù),而后運行查看導(dǎo)出內(nèi)存運行截圖。
調(diào)用python程序內(nèi)存信息輔助說明
這里可以借助python
的memory_profiler
模塊來檢測程序內(nèi)存的占用情況。
安裝memory_profiler
庫:
pip3 install memory_profiler
使用方法很簡單,在需要檢測的函數(shù)或者是代碼前添加@profile
裝飾器即可,例如:
@profile def main(): pass
生成.dat
文件
mprof run <executable>
導(dǎo)出圖示,可以使用
mprof plot --output=filename
python案例代碼
以下2個程序,都是輸出0—9999999之間的數(shù)據(jù),不同的是,第一個程序是使用range
而后給append
進list
中,第二個則是使用迭代器來生成該數(shù)據(jù)。
main.py
程序
@profile def main(): data = list(range(10000000)) for i in data: pass if __name__ == '__main__': main()
main_2.py
程序
def printNum(): i = 0 while i < 10000000: yield i i = i + 1 @profile def main(): for i in printNum(): pass if __name__ == '__main__': main()
運行程序
代碼也有了,就可以按照上述來運行一下程序,并且導(dǎo)出內(nèi)存信息
運行后內(nèi)存信息查看
main.py
運行內(nèi)存圖
main_2.py
運行內(nèi)存圖
如上2張對比圖,當(dāng)我們將數(shù)據(jù)疊加進列表,再輸出的時候,占用內(nèi)存接近400M,而使用迭代器來計算下一個值內(nèi)存僅使用16M。
通過上述案例,我們應(yīng)該知道為什么要使用生成器了吧。
python生成器原理
由于生成器表達式yield
語句涉及到了python
解釋權(quán)內(nèi)部機制,所以很難查看其源碼,很難獲取其原理,不過我們可以利用yield
的暫停機制,來探尋一下生成器。
可以編寫如下代碼:
def testGenerator(): print("進入生成器") yield "pdudo" print("第一次輸出") yield "juejin" print("第二次輸出") def main(): xx = testGenerator() print(next(xx)) print(next(xx)) if __name__ == '__main__': main()
運行后效果如下
通過上述實例,再結(jié)合下面這段生成器的運行過程,會加深對生成器的感觸。
當(dāng)python
遇到yield
語句時,會記錄當(dāng)前函數(shù)的運行狀態(tài),并且暫停執(zhí)行,將結(jié)果拋出。會持續(xù)等待下一次調(diào)用__next__
方法,該方法調(diào)用后,會恢復(fù)函數(shù)的運行,直至下一個yield
語句或者函數(shù)結(jié)束,執(zhí)行到最后沒有yield
函數(shù)可執(zhí)行的時候,會拋StopIteration
來標(biāo)志生成器的結(jié)束。
生成器表達式
在python
中,生成器除了寫在函數(shù)中,使用yield
返回之外,還可以直接使用生成器表達式,額。。??赡芎艹橄?,但是你看下面這段代碼,你就明白了。
def printNums(): for i in [1,2,3,4,5]: yield i def main(): for i in printNums(): print(i) gener = (i for i in [1,2,3,4,5]) for i in gener: print(i) if __name__ == '__main__': main()
其中,代碼(i for i in [1,2,3,4,5])
就等同于printNums
函數(shù),其類型都是生成器,我們可以使用type
打印出來看下。
改下代碼,輸出結(jié)果如下:
好了,生成器表達式其實不復(fù)雜,暫時就講到這里。
總結(jié)
該篇文章是介紹python
生成器,所謂的生成器其實也是一個特殊的迭代器,其底層依然有__iter__
和__next__
方法,不僅如此,它還可以將函數(shù)“暫停”,當(dāng)遇到_next__
后,又從上一次暫停的地方開始執(zhí)行,既然是一個特殊的迭代器,所以還是會引發(fā)StopIteration
異常。而后介紹了為什么需要生成器,距了一個例子,分別打印0——n個數(shù),一個使用生成器 還有一個 使用list
,當(dāng)然list
會更加耗費內(nèi)存,最后介紹了生成器原理 以及 生成器表達式。
到此這篇關(guān)于一文帶你探尋Python中的生成器的文章就介紹到這了,更多相關(guān)Python生成器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Django + Uwsgi + Nginx 實現(xiàn)生產(chǎn)環(huán)境部署的方法
Django的部署可以有很多方式,采用nginx+uwsgi的方式是其中比較常見的一種方式。這篇文章主要介紹了Django + Uwsgi + Nginx 實現(xiàn)生產(chǎn)環(huán)境部署,感興趣的小伙伴們可以參考一下2018-06-06python安裝后無法打開IDLE?Subprocess?Connection?Error的解決方法
有朋友在安裝了Python之后發(fā)現(xiàn)不能正常使用,就說明安裝過程出了問題,下面這篇文章主要給大家介紹了關(guān)于python安裝后無法打開IDLE?Subprocess?Connection?Error的解決方法,需要的朋友可以參考下2023-01-01Pytorch損失函數(shù)nn.NLLLoss2d()用法說明
這篇文章主要介紹了Pytorch損失函數(shù)nn.NLLLoss2d()用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07Python數(shù)據(jù)庫反向生成Model最優(yōu)方案示例
這篇文章主要介紹了Python數(shù)據(jù)庫反向生成Model最優(yōu)方案的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07關(guān)于TensorFlow新舊版本函數(shù)接口變化詳解
今天小編就為大家分享一篇關(guān)于TensorFlow新舊版本函數(shù)接口變化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02