當python中的生成器被完整遍歷一次后,就無法再次遍歷。
創新互聯是專業的錦江網站建設公司,錦江接單;提供網站設計、做網站,網頁設計,網站設計,建網站,PHP網站建設等專業做網站服務;采用PHP框架,可快速的進行錦江網站開發網頁制作和功能擴展;專業做搜索引擎喜愛的網站,專業的做網站團隊,希望更多企業前來合作!
我們希望享有生成器迭代占用內存小的特性,又希望這個生成器能被遍歷多次。
方案之一是使用函數 itertools.tee 來復制生成器
語法: generator1, generator2 = itertools.tee(generator, n=2)
generator是需要復制的生成器, n是復制出生成器個數,默認為2。
我們為了生成器能多次遍歷,可以這樣寫:
generator, copy_generator = itertools.tee(generator, 2)
然后遍歷copy_generator,保存的generator可以再次復制。
1、generator被復制后盡量不要使用
2、如果生成器中迭代的還是生成器,復制最外層生成器,生成器依然只能遍歷一次。
下一篇
python生成器多次遍歷(二)------創建生成器類
Python 中的 yield 關鍵字鮮為人知,但是作用卻很大,正是因為有了yield,才有了Python生成器。
yield 是 Python 的關鍵字,它用于 從函數返回而不破壞其局部變量的狀態 ,并且在調用該函數時,從最后一個 yield 語句開始執行。任何包含 yield 關鍵字的函數都稱為生成器。
Python 中的 yield 關鍵字的作用類似于 Python 中的 return 語句,不同之處在于:
yield的優點
yield的缺點
Python 可以使用 括號() 創建生成器
更多時候,我們使用 yield 關鍵字創建生成器
下面這個生成器,前4次調用它時,返回的是0-3這幾個特殊值,第5次調用它時返回一個10-20之間的隨機整數。
更多時候,生成器可以返回無限的值。
注意 generator() 函數返回的是一個生成器對象,要想獲取它的值,可以像上面那樣在迭代器中取出它的值,我們也可以顯式的調用next函數獲取值。
Python | yield Keyword - GeeksforGeeks:
如果函數要返回一系列結果,我們常見的方法就是將結果放到一份列表中,然后返回給調用者。比如下面的函數,返回字符串中每個單詞的首字母在真個字符串中的索引:
運行結果:
上述的結果完全符合我們的預期,但 get_word_index 函數不夠簡潔。下面我們嘗試使用生成器來實現:
運行結果:
改寫之后,不僅運行結果符合要求,由于不需要和 result 列表交互,函數也變得非常簡潔。下面我們就來詳細學習下生成器吧~
生成器是指使用 yield 表達式的函數,調用生成器函數時,它并不會真的運行,而是會返回迭代器。每次在這個迭代器上面調用內置的 next 函數時,迭代器就會把生成器推進到下一個 yield 表達式那里。生成器傳給 yield 的值均會由迭代器返回給調用者。
此外,如果輸入量非常大,使用列表作為返回值,那么程序就有可能耗盡內存并崩潰。相反,使用生成器之后,則可以應對任意長度的輸入數據。
例如,下面這個生成器函數可以獲取文件中單詞的索引,而不管文件內容多大,該函數執行時消耗的內存,只由單行的文本長度決定:
其中 test_generator.txt 中的內容如下:
運行結果:
下面這句話特別重要: 生成器函數返回的迭代器,是由狀態的,及調用者不應該反復使用它 。我們那 word_index_iter 來說明:
如果想重復調用,請將其封裝成容器:
運行結果:
關于上述自定義容器的實現原理,我的另外一篇文章做了詳細介紹,鏈接奉上:
生成器似乎并不是一個經常被開發者討論的語法,因此也就沒有它的大兄弟迭代器那么著名。大家不討論它并不是說大家都已經對它熟悉到人盡皆知,與之相反,即使是工作多年的開發者可能對生成器的運行過程還是知之甚少。這是什么原因導致的呢?我猜想大概有以下幾點原因: (1)運行流程不同尋常,(2)日常開發不需要,(3)常常將生成器與迭代器混淆。 生成器的運行流程可以按照協程來理解,也就是說 返回中間結果,斷點繼續運行 。這與我們通常對于程序調用的理解稍有差異。這種運行模式是針對什么樣的需求呢? 一般而言,生成器是應用于大量磁盤資源的處理。 比如一個很大的文件,每次讀取一行,下一次讀取需要以上一次讀取的位置為基礎。下面就通過代碼演示具體看看生成器的運行機制、使用方式以及與迭代器的比較。
什么是生成器?直接用文字描述可能太過抽象,倒不如先運行一段代碼,分析這段代碼的運行流程,然后總結出自己對生成器的理解。
從以上演示可以看出,這段代碼定義了一個函數,這個函數除了yield這個關鍵字之外與一般函數并沒有差異,也就是說生成器的魔法都是這個yield關鍵字引起的。 第一點,函數的返回值是一個生成器對象。 上述代碼中,直接調用這個看似普通的函數,然后將返回值打印出來,發現返回值是一個對象,而并不是普通函數的返回值。 第二點,可以使用next對這個生成器對象進行操作 。生成器對象天然的可以被next函數調用,然后返回在yield關鍵字后面的內容。 第三,再次調用next函數處理生成器對象,發現是從上次yield語句之后繼續運行,直到下一個yield語句返回。
生成器的運行流程確實詭異,下面還要演示一個生成器可以執行的更加詭異的操作:運行過程中向函數傳參。
返回生成器和next函數操作生成器已經并不奇怪了,但是在函數運行過程中向其傳參還是讓人驚呆了。 調用生成器的send函數傳入參數,在函數內使用yield語句的返回值接收,然后繼續運行直到下一個yield語句返回。 以前實現這種運行流程的方式是在函數中加上一個從控制臺獲取數據的指令,或者提前將參數傳入,但是現在不用了,send方式使得傳入的參數可以隨著讀取到的參數變化而變化。
很多的開發者比較容易混淆生成器和迭代器,而迭代器的運行過程更加符合一般的程序調用運行流程,因此從親進度和使用熟悉度而言,大家對迭代器更有好感。比如下面演示一個對迭代器使用next方法進行操作。
從以上演示來看,大家或許會認為迭代器比生成器簡單易用得太多了。不過,如果你了解迭代器的實現機制,可能就不會這么早下結論了。python內置了一些已經實現了的迭代器使用確實方便,但是如果需要自己去寫一個迭代器呢?下面這段代碼就帶大家見識以下迭代器的實現。
在python中,能被next函數操作的對象一定帶有__next__函數的實現,而能夠被迭代的對象有必須實現__iter__函數。看了這么一段操作,相信大家對迭代器實現的繁瑣也是深有體會了,那么生成器的實現是不是會讓你覺得更加簡單易用呢?不過千萬別產生一個誤區,即生成器比迭代器簡單就多用生成器。 在實際開發中,如果遇到與大量磁盤文件或者數據庫操作相關的倒是可以使用生成器。但是在其他的任務中使用生成器難免有炫技,并且使邏輯不清晰而導致可讀性下降的嫌疑。 這大概也能解釋生成器受冷落的原因。不過作為一個專業的開發者,熟悉語言特性是分內之事。
到此,關于生成器的討論就結束了。本文的notebook版本文件在github上的cnbluegeek/notebook倉庫中共享,歡迎感興趣的朋友前往下載。