Python中有一個特別晦澀難懂的概念,就是生成器和yield。特別地,如果我們使用scrapy框架進行爬蟲開發,就避不開yield這個關鍵詞,所以,我們需要知道yield是什麼。我們今天不解釋概念,直接用下面的圖說話:
其實這東西的概念就是一句話:定義一個函數,函數中有yield,就是生成器,沒有yield,就是普通函數。
1、yield初步接觸
yield所在的函數(也就是生成器)它的行為和普通函數是不一樣的,我們看下面的這段代碼和它的輸出:
大家看,如果我是一個尋常的p這個函數,它正常列印數字,當我使用yield的時候,它就不會執行函數了,它不列印,而是返回一個generator對象。
2、yield有什麼用
我們只需要牢記一點:生成器返回的對象只有調用它的__next__()方法的時候才會執行,而且它是可迭代的。
先看第一點,調用__next__()方法才會執行:
第9行輸出一個迭代器對象,第10行輸出了具體的數字。
究極之問:這東西這麼設計到底有什麼用?我們從需求入手:
1、現在我要生成10個隨機數,這個在Python中也很簡單:
2、現在我要生成1萬個隨機數。這很簡單,把代碼第5行的10改成10000就好了。但是問題是:對於伺服器程序來說,如果每次請求進來都要創建一個長度為10000的列表存儲數字,內存和CPU開銷都太大了,我們把它改成這樣:
劃重點:第12行代碼執行時並不會立即生成10個數,而是會在第13行的for循環執行的時候再生成。不信?我們改一下看它的結果:
第13行的輸出都出來了,再碰到for循環的時候才會有數據。
歸納總結一下:生成器執行時,遇到yield會出讓CPU,完成生成器的創建,但是只有真正對它進行迭代的時候,才執行具體的操作。scrapy這個爬蟲框架裡就是這麼設計的,它的邏輯就是:我使用生成器的方式先創建1000個任務,先不執行,等到需要執行的時候直接從生成器啟動。
4、寫在最後
生成器的__next__()方法當我們使用for循環遍歷它時,會自動調用,當然,也可以手動調用。
大家看輸出流程,第一次調用have()的__next__(),輸出before之後就回來得到True,並不會執行after,第二次調用have()的__next__()的時候,去執行after,然後準備下一次迭代,因為沒有代碼了,不能進行下一次迭代了,所以拋出異常。
生成器其實確實是一個神器,我們以後如果涉及到並發編程,涉及到爬蟲,還會再碰到它,但是我相信通過本文的學習,大家都已經掌握了。